Retrofit 源码解析 - 动态代理

背景

以前一系列的关于Retrofit使用和封装的讲解事后,想必对Retrofit的灵活性和扩展性有何深刻的了解,既然如此咱们就对于Retrofit内部实现原理来深刻的学习,既然要用就要理解怎么用和怎么能用的的更好,不能局限在使用的层面上,接下来的文章从源码的角度去思考和借鉴如何才能写出一个好的开源框架。html

RxRetrofit封装-专栏java

原理

Retrofit 2.0是如何进行网络请求的呢?主要是用到了Java的动态代理,因此在深刻学习以前,先来了解下Java的动态代理git

何为Java动态代理

其实Java动态代理就是设计模式中的代理模式,特征是代理类与委托类有一样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及过后处理消息等。代理类与委托类之间一般会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象自己并不真正实现服务,而是经过调用委托类的对象的相关方法,来提供特定的服务。 程序员

通俗来讲:好比登陆以前须要判断用户信息是否完整、请求以前须要配置请求数据等等,动态代理就是来处理这样的需求,让用户只须要关心事件处理github

按照代理的建立时期,代理类能够分为两种。 设计模式

  • 静态代理:由程序员建立或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 网络

  • 动态代理:在程序运行时,运用反射机制动态建立而成。 框架

静态代理

静态代理其实就是按照代理模式,在实现了代理类和委托类以后的调用方式,能够把它当作是代理模式的写法eclipse

接口类

/** * 接口 * 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();复制代码

结果

这里写图片描述

动态代理

动态代理,字面可理解在代理类中委托类是动态生成的,及时一个动态代理类可处理多个委托类,从而简化代理类的处理。ide

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-源码

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环境中是可使用这个动态库,因此这里没有显示最后的输出结果,eclipsejava项目是彻底能够的(验证过)

动态代理其实主流的使用是在Spring中,最近一段时间才刚刚流行到Android,其实除了CGlib动态代理之外,还有另一种好用的动态生成思路AOP面向切片

AOP面向切片

想必对OOP再熟悉不过了,那AOP是什么呢?AOP就是把涉及到众多模块的某一类问题进行统一管理,横向的思考需求,而后统一管理处理通用的或者是公用的逻辑,下降项目耦合度,提高效率,简化多余代码,通常AOP结合Aspect(也是Spring技术中大量使用),可是在Android中也有对应的变种AspectJ.

固然因为本文是Retrofit源码解析-动态代理这里就不过多的阐述关于AOP的技术了,有兴趣的同窗能够参考JakeWharton-hugo

JakeWharton-hugo

总结

Retrofit源码解析-动态代理其中使用的就是本文中的第二种动态代理JDK动态代理,因此使得Retrofit在扩展性和维护行方面获得很大的提高,可是其实也是有缺点的,由于JDK动态代理里面过多的使用了反射的机制,因此在效率方面是不及CGlib代理处理的,估计也是因为不支持Android项目,因此被迫选择这种方法,我的理解

专栏

RxJava+Retrofit+OkHttp深刻浅出-终极封装专栏)

源码

下载源码

建议

若是你有任何的问题和建议欢迎加入QQ群告诉我!

相关文章
相关标签/搜索