Retrofit源码解析-动态代理
背景
之前一系列的关于Retrofit
使用和封装的讲解过后,想必对Retrofit
的灵活性和扩展性有何深入的了解,既然如此我们就对于Retrofit
内部实现原理来深入的学习,既然要用就要理解怎么用和怎么能用的的更好,不能局限在使用的层面上,接下来的文章从源码的角度去思考和借鉴如何才能写出一个好的开源框架。
原理
Retrofit
2.0是如何进行网络请求的呢?主要是用到了Java
的动态代理,所以在深入学习之前,先来了解下Java
的动态代理
何为Java
动态代理
其实Java
动态代理就是设计模式中的代理模式,特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
通俗来说:比如登录之前需要判断用户信息是否完整、请求之前需要配置请求数据等等,动态代理就是来处理这样的需求,让用户只需要关心事件处理
按照代理的创建时期,代理类可以分为两种。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
静态代理
静态代理其实就是按照代理模式,在实现了代理类和委托类之后的调用方式,可以把它看成是代理模式的写法
接口类
/**
* 接口
* Created by WZG on 2017/1/19.
*/
public interface HttpRequest {
/**
* 请求
*/
void request();
}
委托类
/**
* 委托类
* Created by WZG on 2017/1/19.
*/
public class HttpRequestImpl implements HttpRequest {
@Override
public void request() {
Log.e("tag","-------->htt请求");
}
}
代理类
/**
* 代理类-静态
* Created by WZG on 2017/1/19.
*/
public class HttpRequestProxy implements HttpRequest {
HttpRequestImpl httpRequest;
public HttpRequestProxy(HttpRequestImpl httpRequest) {
this.httpRequest = httpRequest;
}
@Override
public void request() {
Log.e("tag","静态--------->http请求前");
httpRequest.request();
Log.e("tag","静态--------->http请求后");
}
}
调用
/*静态代理*/
HttpRequestImpl request = new HttpRequestImpl();
HttpRequestProxy proxy = new HttpRequestProxy(request);
proxy.request();
结果
动态代理
动态代理,字面可理解在代理类中委托类是动态生成的,及时一个动态代理类可处理多个委托类,从而简化代理类的处理。
Java
动态代理分为两种
JDK
动态代理-自带CGlib
动态代理-第三方
JDK
动态代理
JDK
动态代理加载器 Proxy
public class Proxy implements Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
throw new RuntimeException("Stub!");
}
public static Class<?> getProxyClass(ClassLoader loader, Class... interfaces) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
public static boolean isProxyClass(Class<?> cl) {
throw new RuntimeException("Stub!");
}
public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
}
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器;
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的;
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类;
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。
这里使用第三种AppClassLoader
,主要使用newProxyInstance
方法去加载
参数 | 含义 |
---|---|
ClassLoader loader | 指被代理的对象 |
Class[] interfaces | 要调用的方法 |
InvocationHandler h | 方法调用时所需要的参数 |
JDK
动态代理类处理方法主要是依赖InvocationHandler
接口
public interface InvocationHandler {
Object invoke(Object var1, Method var2, Object[] var3) throws Throwable;
}
invoke
方法是动态代理类处理的主要方法
参数 | 含义 |
---|---|
Object var1 | 指被代理的对象 |
Method var2 | 要调用的方法 |
Object[] var3 | 方法调用时所需要的参数 |
代理类实现
共用HttpRequest
接口,不在重复描述
/**
* 动态代理-jdk
* Created by WZG on 2017/1/19.
*/
public class HttpRequestJDKProxy implements InvocationHandler {
private Object target;
/**
* 绑定委托对象并返回一个代理类
* @param target
* @return
*/
public Object bind(Object target) {
this.target = target;
//取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this); //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Log.e("tag","jdk--------->http请求前");
Object result=method.invoke(target, objects);
Log.e("tag","jdk--------->http请求后");
return result;
}
}
调用
/*jdk动态代理*/
HttpRequestJDKProxy jdkProxy = new HttpRequestJDKProxy();
HttpRequest httpRequest = (HttpRequest) jdkProxy.bind(request);
httpRequest.request();
结果
CGlib
动态代理
CGlib
动态代理其实是国外一个开源作者写的一个开源库,帮助完善jdk
动态代理中
要绑定接口(这是一个缺陷)
Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
JDK
的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib
是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final
修饰的类进行代理
CGlib
代理类
/**
* cglib动态代理类
* Created by WZG on 2017/1/19.
*/
public class HttpRequestCglibProxy implements MethodInterceptor {
/**
* 创建代理对象
*
* @param target
* @return
*/
public Object getInstance(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Log.e("tag", "Cglib--------->http请求前");
Object result= methodProxy.invokeSuper(o, objects);
Log.e("tag", "Cglib--------->http请求后");
return result;
}
}
调用
/*cglib动态代理*/
HttpRequestCglibProxy httpRequestCglibProxy=new HttpRequestCglibProxy();
HttpRequest httpRequestCglib = (HttpRequest) httpRequestCglibProxy.getInstance(new HttpRequestCglibImpl());
httpRequestCglib.request();
但是由于Android
中和java
环境的不一,导致在Android项目中其实并不能使用CGlib
这种动态代理方式,在java
环境中是可以使用这个动态库,所以这里没有显示最后的输出结果,eclipse
的java
项目是完全可以的(验证过)
动态代理其实主流的使用是在Spring
中,最近一段时间才刚刚流行到Android
,其实除了CGlib
动态代理以外,还有另外一种好用的动态生成思路AOP
面向切片
AOP
面向切片
想必对OOP
再熟悉不过了,那AOP
是什么呢?AOP就是把涉及到众多模块的某一类问题进行统一管理,横向的思考需求,然后统一管理处理通用的或者是公用的逻辑,降低项目耦合度,提升效率,简化多余代码,一般AOP
结合Aspect
(也是Spring技术中大量使用),但是在Android中也有对应的变种AspectJ
.
当然由于本文是Retrofit源码解析-动态代理
这里就不过多的阐述关于AOP
的技术了,有兴趣的同学可以参考JakeWharton
-hugo
总结
Retrofit
源码解析-动态代理其中使用的就是本文中的第二种动态代理JDK
动态代理,所以使得Retrofit
在扩展性和维护行方面得到很大的提升,但是其实也是有缺点的,因为JDK
动态代理里面过多的使用了反射的机制,所以在效率方面是不及CGlib
代理处理的,估计也是由于不支持Android
项目,所以被迫选择这种方法,个人理解