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羣告訴我!

相關文章
相關標籤/搜索