4.從架構角度看Retrofit的做用、原理和啓示
3.Retrofit 框架源碼學習
2.Retrofit分析-經典設計模式案例
1.Retrofit分析-漂亮的解耦套路php
=================css
4.從架構角度看Retrofit的做用、原理和啓示java
Retrofit是squareup公司的開源力做,和同屬squareup公司開源的OkHttp,一個負責網絡調度,一個負責網絡執行,爲Android開發者提供了即方便又高效的網絡訪問框架。android
不過,對於Retrofit這樣設計精妙、代碼簡潔、使用方便的優秀開源項目,不能僅知道如何擴展和使用,或者僅研究它採用的技術或模式,git
「技」固然重要,但不能忽視了背後的「道」。github
對於Retrofit,咱們還應該看到的,是她在優化App架構方面的努力,以及她在提高開發效率方面的借鑑和啓示。算法
本文試圖經過一個具體場景,先總結Retrofit在架構中起到的做用,再分析其實現原理,最後探討Retrofit給咱們帶來的啓示。數據庫
咱們先經過一個簡單的應用場景來回顧Retrofit的使用過程。編程
一般來講,使用Retrofit要通過這樣幾個步驟json
compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:retrofit-converters:2.3.0' compile 'com.squareup.retrofit2:retrofit-adapters:2.3.0'
若是須要使用更多擴展功能,好比gson轉換,rxjava適配等,能夠視本身須要繼續添加引用
compile 'com.squareup.retrofit2:converter-gson:2.3.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
若是現有的擴展包不能知足須要,還能夠本身擴展converter,adapter等。
public interface INetApiService { @GET("/demobiz/api.php") Call<BizEntity> getBizInfo(@Query("id") String id); }
在這個接口定義中,用註解@GET("/demobiz/api.php")聲明瞭url路徑,用註解@Query("id") 聲明瞭請求參數。
最重要的是,用Call<BizEntity>聲明瞭返回值是一個Retrofit的Call對象,而且聲明瞭這個對象處理的數據類型爲BizEntity,BizEntity是咱們自定義的數據模型。
//新建一個Retrofit對象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN)//要訪問的網絡地址域名,如http://www.zhihu.com .addConverterFactory(GsonConverterFactory.create()) .build(); ... //用retrofit加工出對應的接口實例對象 INetApiService netApiService= retrofit.create(INetApiService.class); //能夠繼續加工出其餘接口實例對象 IOtherService otherService= retrofit.create(IOtherService.class); ··· //調用接口函數,得到網絡工做對象 Call<BizEntity> callWorker= netApiService.getBizInfo("id001");
這個複雜的過程下來,最終獲得的callWorker對象,才能夠執行網絡訪問。
callWorker.enqueue(new Callback<BizEntity>() { @Override public void onResponse(Call<BizEntity> call, Response<BizEntity> response) {...} @Override public void onFailure(Call<BizEntity> call, Throwable t) {...} });
在回調函數裏,取得咱們須要的BizEntity數據對象。
網絡訪問結束。
咱們從上面的應用場景能夠看出,Retrofit並不作網絡請求,只是生成一個能作網絡請求的對象。
Retrofit的做用是按照接口去定製Call網絡工做對象。
什麼意思?就是說:
Retrofit不直接作網絡請求
Retrofit不直接作網絡請求
Retrofit不直接作網絡請求
重要的事情說三遍。
網絡請求的目標雖然是數據,可是咱們須要爲這個數據寫大量的配套代碼,發起請求的對象Call,接收數據的對象CallBack,作數據轉換的對象Converter,以及檢查和處理異常的對象等。
這對於一個項目的開發、擴展和維護來講,都是成本和風險。
而Retrofit作的事情,就是爲開發者節省這部分的工做量,Retrofit一方面從底層統一用OkHttp去作網絡處理;
另外一方面在外層靈活提供能直接融入業務邏輯的Call網絡訪問對象。
具體來講,Retrofit只負責生產對象,生產能作網絡請求的工做對象,他有點像一個工廠,只提供產品,工廠自己不處理網絡請求,產品才能處理網絡請求。
Retrofit在網絡請求中的做用大概能夠這樣理解:
咱們看到,從一開始,Retrofit要提供的就是個Call工做對象。
換句話說,對於給Retrofit提供的那個接口
public interface INetApiService { @GET("/demobiz/api.php") Call<BizEntity> getBizInfo(@Query("id") String id); }
這個接口並非傳統意義上的網絡請求接口,這個接口不是用來獲取數據的接口,而是用來生產對象的接口,這個接口至關於一個工廠,接口中每一個函數的返回值不是網絡數據,而是一個能進行網絡請求的工做對象,咱們要先調用函數得到工做對象,再用這個工做對象去請求網絡數據。
因此Retrofit的實用價值意義在於,他能根據你的接口定義,靈活地生成對應的網絡工做對象,而後你再擇機去調用這個對象訪問網絡。
理解了這一點,咱們才能去擴展Retrofit,並理解Retrofit的設計思想。
咱們先來看Retrofit能擴展哪些功能,而後再去理解Retrofit的工做原理。
Retrofit主要能夠擴展三個地方:
OkHttpClient mClient = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { try { Request.Builder builder = chain.request().newBuilder(); builder.addHeader("Accept-Charset", "UTF-8"); builder.addHeader("Accept", " application/json"); builder.addHeader("Content-type", "application/json"); Request request = builder.build(); return chain.proceed(request); } catch (Exception e) { e.printStackTrace(); } return null; } }).build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .client(mClient) .build();
擴展的是對返回的數據類型的自動轉換,把一種數據對象轉換爲另外一種數據對象。
在上述場景中,GsonConverterFactory能夠把Http訪問獲得的json字符串轉換爲Java數據對象BizEntity,這個BizEntity是在INetApiService接口中要求的的。
這種轉換咱們本身也常常作,很好理解。
若是現有的擴展包不能知足須要,能夠繼承Retrofit的接口。retrofit2.Converter<F,T>,本身實現Converter和ConverterFactory。
在建立Retrofit對象時,能夠插入咱們自定義的ConverterFactory。
//retrofit對象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(YourConverterFactory.create())//添加自定義Converter .build();
擴展的是對網絡工做對象callWorker的自動轉換,把Retrofit中執行網絡請求的Call對象,轉換爲接口中定義的Call對象。
這個轉換不太好理解,咱們能夠對照下圖來理解:
Retrofit自己用一個OkHttpCall的類負責處理網絡請求,而咱們在接口中定義須要定義不少種Call,例如Call<BizEntity>,或者Flowable<BizEntity>等,接口裏的Call和Retrofit裏的OkHttpCall並不一致,因此咱們須要用一個CallAdapter去作一個適配轉換。
(Retrofit底層雖然使用了OkHttpClient去處理網絡請求,但她並無使用okhttp3.call這個Call接口,而是本身又建了一個retrofit2.Call接口,OkHttpCall繼承的是retrofit2.Call,與okhttp3.call只是引用關係。
這樣的設計符合依賴倒置原則,能夠儘量的與OkHttpClient解耦。)
這實際上是Retrofit很是核心,也很是好用的一個設計,若是咱們在接口中要求的函數返回值是個RxJava的Flowable對象
public interface INetApiService { @GET("/demobiz/api.php") Flowable<BizEntity> getBizInfo(@Query("id") String id); }
那麼咱們只須要爲Retrofit添加對應的擴展
//retrofit對象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build();
就能獲得Flowable類型的callWorker對象
//用retrofit加工出對應的接口實例對象 INetApiService netApiService= retrofit.create(INetApiService.class); ··· //調用接口函數,得到網絡工做對象 Flowable<BizEntity> callWorker= netApiService.getBizInfo("id001");
在這裏,callAdapter作的事情就是把retrofit2.Call對象適配轉換爲Flowable<T>對象。
一樣,若是現有的擴展包不能知足須要,能夠繼承Retrofit的接口retrofit2.CallAdapter<R,T>,本身實現CallAdapter和CallAdapterFactory。
Retrofit當然設計精妙,代碼簡潔,使用方便,但相應的,咱們要理解Retrofit的實現原理也不太容易,這麼精妙的設計是極佳的研究素材,咱們不能僅僅停留在知道怎麼使用,怎麼擴展的階段,那實在是對這個優秀開源項目的浪費。
其實,Retrofit使用的,就是動態代理,方法註解、建造者和適配器等成熟的技術或模式,可是因爲她的設計緊湊,並且動態代理屏蔽了不少過程上的細節,因此比較難以理解。
從前面的使用場景可知,retrofit會生成一個接口實例。
//用retrofit加工出對應的接口實例對象 INetApiService netApiService= retrofit.create(INetApiService.class);
到Retrofit源碼裏看create函數,是一個動態代理。
public <T> T create(final Class<T> service) { ... return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { ... ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } }); }
要理解動態代理,最好要看到動態生成的代理類。
因爲動態代理是在運行時動態生成的代理類,用常規的反編譯方法沒法查看,通常要使用Java提供的sun.misc.ProxyGenerator.generateProxyClass(String proxyName,class[] interfaces)函數生成代理類,函數會返回byte[]字節碼,而後對字節碼反編譯獲得Java代碼。
有一個小問題是,AndroidStudio並不提供sun.misc這個包,咱們須要用IntelliJ或者Eclipse創建一個Java工程,在Java環境裏調用這個函數。
拿到的代理類,大概是這樣的:
public final class INetApiService extends Proxy implements INetApiService { ...//一些Object自帶方法 private static Method m3;//接口定義的方法 static { try { //Object自帶方法的初始化 m0,m1,m2 = ... //接口中定義的方法 m3 = Class.forName("com.demo.net$INetApiService")//反射接口類 .getMethod("getBizInfo",//反射函數 new Class[] { Class.forName("java.lang.String") });//反射參數 //接口中定義的其餘方法 ... } ... } //返回接口實例對象 public INetApiService (InvocationHandler invocationHandler){ super(invocationHandler); } // public final Call getBizInfo(String str){ ... try{//用Handler去調用 return (Call)this.h.invoke(this, m3, new Object[]{str}); } } }
咱們能夠看到,代理類生成的是一個INetApiService接口的實例對象,該對象的getBizInfo函數返回的是接口中定義的Call網絡工做對象,
這也體現了Retrofit的核心價值,生成接口定義的Call網絡工做對象。
那麼,這個Call網絡工做對象是如何生成的呢,上面動態代理生成的代碼是這樣的:
return (Call)this.h.invoke(this, m3, new Object[]{str});
也就是說,這個Call網絡工做對象是在InvocationHandler中實現的,也就是在Retrofit.create函數中,由InvocationHandler實現的。
這樣咱們就明白了,Retrofit使用動態代理,實際上是爲了開發者在寫代碼時方便調用,而真正負責生產Call網絡工做對象的,仍是Retrofit.create函數中定義的這個InvocationHandler,這個InvocationHandler的代碼咱們再貼一遍:
new InvocationHandler() { ... ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); }
ServiceMethod能讓咱們準確解析到INetApiService中定義的函數,爲最後的適配轉換提供轉換目標,詳細分析咱們後面再說,先看適配轉換的過程。
咱們看到,Retrofit內部默認使用OkHttpCall對象去處理網絡請求,可是返回的網絡工做對象是通過適配器轉換的,轉換成接口定義的那種Call網絡工做對象。
這個適配轉換,就是Retrofit能按照接口去定製Call網絡工做對象的祕密。
咱們在初始化Retrofit對象時,好像不添加CallAdapterFactory也能實現適配轉換。
//retrofit對象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) //能夠不添加CallAdapterFactory .build();
這是怎麼回事呢,咱們知道Retrofit使用了建造者模式,建造者模式的特定就是實現了建造和使用的分離,因此建造者模式的建造函數裏,
通常會有很複雜的對象建立和初始化過程,因此咱們要看一下Retrofit的build函數。
public Retrofit build() { ... okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient();//使用OkHttpClient處理網絡請求 } ... //根據當前運行平臺,設置默認的callAdapterFactory adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); ... return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }
這段代碼裏,咱們看到Retrofit使用OkHttpClient處理網絡請求,而且會添加默認的callAdapterFactory,這個platform是一個簡單工廠,能根據當前系統平臺去生成對應的callAdapterFactory
private static Platform findPlatform() { try { Class.forName("android.os.Build"); if (Build.VERSION.SDK_INT != 0) { return new Android();//根據當前系統平臺返回相應的對象 } ... } ... static class Android extends Platform { ... @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) { if (callbackExecutor == null) throw new AssertionError(); return new ExecutorCallAdapterFactory(callbackExecutor); } ... }
這個Platform是Retrofit在Builder的構造函數裏初始化的。
因此,在Retrofit.build()函數中,咱們爲Retrofit默認添加的callAdapterFactory,是在Platform中爲Android系統設定的ExecutorCallAdapterFactory。
咱們看ExecutorCallAdapterFactory的代碼,這是一個工廠類,能夠返回CallAdapter對象:
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { ... return new CallAdapter<Object, Call<?>>() { ... // 轉換後 轉換前,也就是OkHttpCall @Override public Call<Object> adapt(Call<Object> call) { return new ExecutorCallbackCall<>(callbackExecutor, call); } }; }
在adapt函數中,適配器會把Retrofit中用來訪問網絡的OkHttpCall,轉換爲一個ExecutorCallbackCall(繼承了INetApiService接口裏要求返回的網絡工做對象retrofit2.Call),
這個例子裏面,因爲OkHttpCall和ExecutorCallbackCall都實現了retrofit2.Call接口,結果出現了從Call<Object>轉換爲Call<Object>的狀況,這可能不容易理解,咱們換個RxJava2CallAdapterFactory來看看
//RxJava2CallAdapterFactory中 @Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { ... return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable, isSingle, isMaybe, false); } //RxJava2CallAdapter中 // 轉換後 轉換前,也就是OkHttpCall @Override public Object adapt(Call<R> call) { ... Observable<?> observable; ... return observable; }
這個CallAdapter的轉換就比較明顯了,把retrofit2.Call對象經過適配器轉換爲了一個實爲Observable<?>的Object對象。
至此,咱們能夠理解Retrofit根據接口定義動態生產Call網絡請求工做對象的原理了,其實就是經過適配器把retrofit2.Call對象轉換爲目標對象。
至於適配器轉換過程當中,如何實現的對象轉換,就能夠根據需求來自由實現了,好比利用靜態代理等,若有必要,咱們能夠自行開發擴展,Retrofit框架並不限制咱們對於適配器的實現方式。
在前面分析中,咱們知道了Retrofit的總體工做流程,就是Retrofit用動態代理生成Call網絡請求對象,在這個過程當中,用適配器把Retrofit底層的retrofit2.Call對象轉換爲INetApiService中定義的Call網絡請求對象(如Flowable)。
問題是,Retrofit具體是如何知道了INetApiService中定義的Call網絡請求對象,如何實現網絡請求,以及如何執行的數據轉換呢?
具體過程以下;
首先,根據INetApiService中定義的函數,解析函數,獲得函數的具體定義,並生成對應的ServiceMethod。
而後,根據這個ServiceMethod,實現一個OkHttpCall的Call對象,負責在Retrofit底層實現網絡訪問。
其中,在網絡訪問返回了網絡數據時,根據ServiceMethod實現數據轉換。
最後,利用上一小節中匹配的適配器,把OkHttpCall對象轉換爲INetApiService要求的Call網絡請求對象。
因此,咱們要了解的就是函數解析、網絡請求和數據轉換這三個動做,至於最後的適配轉換,在上一節中已經分析過了。
1. 函數解析
在接口函數裏,用註解描述了輸入參數,用Java對象定義了返回值類型,因此對輸入參數和返回值,ServiceMethod採起了不一樣的方式去處理。
輸入參數
輸入參數是用來描述url的,它的處理相對簡單,ServiceMethod會根據反射獲得的Method,取得Annotation註解信息,這些註解是Retrofit本身預約義好的(retrofit2.http.*),ServiceMethod根據預先的定義,直接判斷註解所屬的邏輯分支,在有網絡請求時分狀況進行處理,就能獲得目標url,http請求頭等數據。
返回值
返回值是須要用CallAdapter去適配的,因此核心在於生成對應的CallAdapter。
在Retrofit生成Call網絡工做對象時,她經過動態代理獲取到了接口函數的Method定義,從這個Method中能夠獲取函數定義的返回對象類型,因爲這個轉換是須要CallAdapterFactory生產CallAdapter對象去實現,而Retrofit事先並不知道要使用哪一個Factory,因此她是遍歷全部的CallAdapterFactory,根據目標函數的返回值類型,讓每一個Factory都去嘗試生產一個CallAdapter,哪一個成功就用哪一個。
2. 網絡請求
OkHttpCall繼承的retrofit2.Call接口是爲了依賴倒置解耦的,真正的網絡請求是由OkHttpCall內部引用的okhttp3.call處理的,這個okhttp3.call是
借道ServiceMethod獲取的Retrofit中的callFactory,也就是Retrofit中的OkHttpClient。
整個引用鏈條是這樣的:
OkHttpCall--okhttp3.call
-->
ServiceMethod--callFactory
-->
Retrofit.build()--callFactory//(如未擴展賦值)new OkHttpClient();
-->
Retrofit.Builder().client(mClient)//(可能有擴展賦值)擴展過的OkHttpClient
最終的網絡請求是由OkHttpCall調用OkHttpClient發出的,調用和回調等過程,也就是在OkHttpCall中處理的。
網絡請求的生成過程當中,爲了使用接口函數中定義的參數,OkHttpCall會調用ServiceMethod來生成Request請求對象,再交給OkHttpCall去處理。
3. 數據轉換
由於回調是在OkHttpCall中處理的,因此對回調數據的轉換也在OkHttpCall中觸發,爲了符合接口函數中定義的返回數據類型,OkHttpCall會調用ServiceMethod來轉換Response返回數據對象。
OkHttpCall對返回的網絡數據,會調用一個serviceMethod.toResponse(ResponseBody body)函數,函數中執行的是:
R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); }
這個函數能夠把原始的okhttp3. ResponseBody數據轉換爲INetApiService接口中要求的數據類型(如BizEntity類型)。
從代碼能夠看出,實現數據轉換的核心對象實際上是responseConverter,這個Converter實際上要依次通過Retrofit的建造和ServiceMethod的建造後,才能肯定下來的。
Retrofit建造時添加數據轉換工廠
Retrofit裏有converterFactries列表,這是在咱們初始化Retrofit實例時添加的
//retrofit對象 Retrofit retrofit=new Retrofit.Builder() .baseUrl(Config.DOMAIN) .addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(YourConverterFactory.create())//添加自定義Converter .build();
ServiceMethod建造時設定數據轉換器
ServiceMethod在建造時,就已經肯定了對應的是INetApiService中的哪一個函數,因此須要明確設定本身的Converter<R,T>轉換對象
public ServiceMethod build() { ... responseConverter = createResponseConverter(); ... }
這須要調用Retrofit
private Converter<ResponseBody, T> createResponseConverter() { ... retrofit.responseBodyConverter(responseType, annotations); }
Retrofit會在本身的轉換器工廠列表中遍歷每一個ConverterFactory,嘗試根據ServiceMethod所對應的目標數據類型,找到Converter數據轉換類
for (int i = start, count = converterFactories.size(); i < count; i++) { Converter<ResponseBody, ?> converter = converterFactories.get(i).responseBodyConverter(type, annotations, this); if (converter != null) { //noinspection unchecked return (Converter<ResponseBody, T>) converter; } }
以Gson轉換爲例,GsonConverterFactory會經過getAdapter來嘗試匹配目標數據類型:
public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {...}
若是能夠匹配,那麼前面調用serviceMethod.toResponse(ResponseBody body)函數時,會調用
R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); }
在調用這段代碼時,其實就是調用了Gson中最終執行數據轉換的代碼:
@Override public T convert(ResponseBody value) throws IOException { JsonReader jsonReader = gson.newJsonReader(value.charStream()); try { return adapter.read(jsonReader); } finally { value.close(); } }
總結來講,Retrofit在類的單一職責方面分隔的很好,OkHttpCall類只負責網絡交互,凡是須要知道函數定義的,都交給ServiceMethod類去處理,而ServiceMethod類對使用者不公開,由於Retrofit是個外觀模式,而全部須要擴展的都在Retrofit的建造者中實現,他們的分工大概是這樣的:
這三個類分工合做,共同實現了函數解析、網絡訪問和數據轉換,並保留了良好的可擴展性。
至此,Retrofit的實現細節就已經基本清楚了,他用動態代理去定製接口定義的Call網絡工做對象,用適配器去把底層的Call對象轉換爲目標Call對象,用函數解析/OkHttpClient/數據轉換等實現對Call對象的適配轉換,並能處理真正的網絡請求。
這裏面涉及的總體結構和角色分工,大概能夠這樣表示:
其中,擴展適配器、擴展數據轉換和擴展OkHttpClient,雖然都是經過Retrofit實現擴展,但真正的使用者是Retrofit內部的ServiceMethod、OkHttpCall和okhttp3.call等類或對象。
若是咱們不直接正面分析Retrofit的結構設計和技術細節,而是先從Retrofit的功能和做用入手,倒過來推測Retrofit的目標,進而分析其架構和搭建細節,Retrofit爲何會設計成這樣就很好理解了。
Retrofit的功能是按照接口定義,自動定製Call網絡工做對象,因此Retrofit的目標應該就是避免爲網絡訪問開發大量的配套代碼。
爲了實現這一目標,Retrofit須要分析哪些是易變的,哪些是不變的,而後分別處理。
因爲Retrofit提供網絡訪問的工做對象,又是服務於具體業務,因此能夠分網絡訪問和具體業務兩部分來分析。
網絡訪問的不變性
對於網絡訪問來講,不變的是必定有一個實現網絡訪問的對象,Retrofit選用了自家的OkHttpClient,不過爲了把Retrofit和OkHttp兩個項目解耦合,Retrofit根據依賴倒置原則,定義了Retrofit本身的Call即retrofit2.call,並定義了操做網絡請求的OkHttpCall。
網絡訪問的易變性
對於網絡訪問來講,易變的是網絡訪問的url、請求方式(get/post等)、Http請求的Header設置與安全設置等,以及返回的數據類型。
針對易變的url和請求方式,Retrofit使用了方法註解的方式,可讀性良好,擴展性優異,但這須要實現對接口函數中註解的解析,這樣就有了ServiceMethod。
針對Http請求的各類設置,其實Retrofit沒作什麼,由於Retrofit使用的OkHttp有攔截器機制,能夠應付這種變化。
針對返回的數據類型,因爲目標數據類型與業務有關,是不肯定的,Retrofit沒法提供一個萬能的轉換類,因此Retrofit提供了擴展接口,容許開發者本身定義ConverterFactory和Converter,去實現潛在的數據類型轉換。
具體業務的不變性
對於具體業務來講,不變的是必定要有一個Call網絡工做對象,因此Retrofit能夠有一個生產對象的機制(像工廠同樣)
具體業務的易變性
對於具體業務來講,易變的就是這個Call網絡工做對象的類型,不只有CallBacl回調、可能還有Flowable工做流、或者其餘潛在的對象類型。
針對這種Call對象的易變性,Retrofit也是沒法提供一個萬能的實現類,因此也是提供了擴展解耦,容許開發者本身定義CallAdapterFactory和CallAdapter,去實現潛在的Call類型轉換。
由於這種Call對象的生產須要有大量的配套代碼,爲了簡化代碼,Retrofit使用動態代理來生產這個對象。
最後,由於須要處理的方法和對象太多太複雜,須要使用建造者模式來把建造過程和使用過程分離開。
這樣倒着走一遍以後,咱們再看Retrofit的設計和實現原理,就會以爲水到渠成,對於Retrofit精妙的設計更會有一種切身體會。
在上文的反推過程當中,咱們可窺見(瞎猜)Jake大神的一些思路:
最後感嘆一下。
對於網絡訪問的抽象與優化,其實是個很是難的課題,在Retrofit以前,你們努力的方向基本上都是Volley/OkHttp這種圍繞底層網絡訪問的工做。
由於越底層的東西越容易抽象,越上升到接近業務層,就越容易在紛擾的業務層中迷失。
Retrofit能精準地抓到Call網絡工做對象這個關鍵點,並能經過一系列精巧的設計實現對這種類型「飄忽不定」的對象的自動化定製生產,着實使人讚歎。
Retrofit
你真的會用Retrofit2嗎?Retrofit2徹底教程
Retrofit2 源碼解析
Retrofit 框架源碼學習
拆輪子系列:拆 Retrofit
Android 動態代理以及利用動態代理實現 ServiceHook
--------------------
3.Retrofit 框架源碼學習
Retrofit 是 Square 安卓平臺網絡層三板斧最後一個項目,Retrofit 依賴 OkHttp 。Retrofit 讓 http 網絡請求更加清晰。
聲明一個接口,並用接口描述 request
public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); }
方法上面的註釋表示 request 的接口名 ,方法的返回類型就是 http 請求的返回值,方法的參數就是 http 的請求參數。
建立一個 Retrofit 客戶端
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); GitHubService service = retrofit.create(GitHubService.class);
Retrofit 建立的時候指定了 request 的接口地址,而後調用 retrofit.create 方法建立一個 GitHubService 實例。
發起網絡請求
Call<List<Repo>> repos = service.listRepos("octocat"); repos.execute().body()
上面的例子能夠看到,retrofit.create() 方法會建立一個 GitHubService 實例,可是 GitHubService 自己是一個接口。
爲了瞭解 retrofit.create() 方法,咱們先看下 Retrofit 的建立過程。
Retrofit 和 OkHttp 同樣都是使用構建者模式建立對象。先看下 Retrofit.Builder 的 build() 方法。
public Retrofit build() { if (baseUrl == null) { throw new IllegalStateException("Base URL required."); } okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient(); } Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); } // Make a defensive copy of the adapters and add the default Call adapter. List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // Make a defensive copy of the converters. List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories); return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); }
建立 Retrofit 的時候須要傳遞一下幾個參數
1. callFactory 用來建立一個實現了 okhttp3.Call.Factory 的對象,若是沒有設置,默認爲 OkHttpClient。 2. baseUrl 網絡接口的地址。 3. converterFactories 用來把服務器返回的數據轉換爲對象。 4. adapterFactories 用來發起網絡請求。 5. callbackExecutor 是一個調度器,用來接收返回的數據,在 Android 上默認是封裝了 handler 的 MainThreadExecutor 6. validateEagerly 是一個開關,若是爲 true 會緩存建立的 ServiceMethod 。
public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service}, new InvocationHandler() { private final Platform platform = Platform.get();
這裏用到了一個公共技術點之 Java 動態代理,create 方法傳入一個 Class ,這個 Class 對象就是上文的 GitHubService 的 Class 。
GitHubService 的方法是由 InvocationHandler 代理實現的,重點看三行代碼
……
ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall);
ServiceMethod<?, ?> loadServiceMethod(Method method) { ServiceMethod<?, ?> result = serviceMethodCache.get(method); if (result != null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { result = new ServiceMethod.Builder<>(this, method).build(); serviceMethodCache.put(method, result); } } return result; }
這裏建立了一個 ServiceMethod 對象。
OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) { this.serviceMethod = serviceMethod; this.args = args; }
建立了一個 OkHttpCall ,serviceMethod 和 args 是 OkHttpCall 的成員函數。
因此,
這裏須要明白 serviceMethod.callAdapter 是怎麼來的
1. 在 ServiceMethod.Builder.build() 中調用 createCallAdapter() 2. 在 createCallAdapter() 中會找到 (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations) 3. 在 callAdapter() 中調用 nextCallAdapter 4. nextCallAdapter 會遍歷 adapterFactories 返回一個 CallAdapter。
這裏再回頭看下 adapterFactories Retrofit.Builder.build() 方法中
List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
而在 Retrofit.nextCallAdapter() 中
int start = adapterFactories.indexOf(skipPast) + 1; for (int i = start, count = adapterFactories.size(); i < count; i++) { CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this); if (adapter != null) { return adapter; } }
若是沒有設置 AdapterFactory 將會使用一個默認的 AdapterFactory
CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) { if (callbackExecutor != null) { return new ExecutorCallAdapterFactory(callbackExecutor); } return DefaultCallAdapterFactory.INSTANCE; }
因此若是咱們設置了一個 RxJavaCallAdapterFactory,就會返回 RxJavaCallAdapterFactory。
經過 retrofit.create() 咱們能夠知道,retrofit.create() 返回的是一個代理對象InvocationHandler ,那麼在執行
Call<List<Repo>> repos = service.listRepos("octocat");
方法時,調用的其實是 callAdapter.adapt(okHttpCall),以 DefaultCallAdapterFactory 爲例
結合 retrofit.create() 方法能夠得知這裏返回的是一個 OkHttpCall 對象。
接下來使用 OkHttpCall.execute() 或者 異步執行 enqueue(Callback<T> callback)
這兩種方式都會調用 createRawCall() 建立一個 okhttp3.Call
private okhttp3.Call createRawCall() throws IOException { Request request = serviceMethod.toRequest(args); okhttp3.Call call = serviceMethod.callFactory.newCall(request); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; }
此處的 serviceMethod.callFactory 就是 retrofit.create() 中建立的 OkHttpClient()
後面的內容都是由 Okhttp 模塊接管,進行網絡請求,參考okHttp 框架源碼學習
而後調用 parseResponse(call.execute())
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException { ResponseBody rawBody = rawResponse.body(); …… 省略一些 http 返回值處理邏輯 …… try { T body = serviceMethod.toResponse(catchingBody); return Response.success(body, rawResponse); } …… }
okHttp 請求網絡的返回數據,會交給 serviceMethod.toResponse
R toResponse(ResponseBody body) throws IOException { return responseConverter.convert(body); }
在 ServiceMethod.Builder.build() 方法中能夠找到 responseConverter 是經過 createResponseConverter() 方法的返回對象。
createResponseConverter() 只是報包裹了 retrofit.responseBodyConverter(responseType, annotations) 方法。
retrofit.responseBodyConverter() 繼續跟蹤下去會得知,返回的是 converterFactories 數組的第 0 個對象,也就是內置的 BuiltInConverters.responseBodyConverter() 方法返回的 BufferingResponseBodyConverter
static final class BufferingResponseBodyConverter implements Converter<ResponseBody, ResponseBody> { static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter(); @Override public ResponseBody convert(ResponseBody value) throws IOException { try { // Buffer the entire body to avoid future I/O. return Utils.buffer(value); } finally { value.close(); } } }
再看一下 Utils.buffer(value)
static ResponseBody buffer(final ResponseBody body) throws IOException { Buffer buffer = new Buffer(); body.source().readAll(buffer); return ResponseBody.create(body.contentType(), body.contentLength(), buffer); }
最終會返回一個從新封裝的 Okhttp 框架的 ResponseBody 對象。
2.Retrofit分析-經典設計模式案例
retrofit就是對okhttp再作了一層封裝。
本篇是retrofit番外篇。只講retrofit中的設計模式以及我我的的理解與延伸。若是你還沒看過retrofit源碼,不妨先看看這篇Retrofit分析-漂亮的解耦套路。
之前用的volley, async-http-lib, xUtils。這些libs基本都是上圖這個workflow。向服務器請求API總共分三步。
現在的retrofit也是換湯不換藥的。也是這三步。
CallAdapter
(你能夠把它理解成executor)Converter
(解析數據並轉換成T)原本還應該有個CallFactory
來切換具體的http client的。就像volley那樣>=9使用HttpUrlConnection
, <9使用HttpClient
。
不過我想都是square出品,彼此互推也是理所固然的啊。我相信之後okhttp確定是惟一的請求client。
上面說CallAdapter
能夠理解成executor,具體是什麼,咱們下面講具體設計模式時再詳細討論。
Retrofit給咱們暴露的方法和類很少。核心類就是Retrofit,咱們只管配置Retrofit,而後作請求。剩下的事情就跟上層無關了,只須要等待回調。這樣大大下降了系統的耦合度。對於這種寫法,咱們叫外觀模式(門面模式)。
幾乎全部優秀的開源library都有一個門面。好比Glide.with()
ImageLoader.load()
Alamofire.request()
。有個門面方便記憶,學習成本低,利於推廣品牌。 Retrofit的門面就是retrofit.create()
當咱們本身寫的代碼的時候儘可能也要這樣來作。
好比咱們有一個獨立並公用的模塊,須要供其餘模塊來調用。好比download
,location
,socialshare
等
最好咱們寫一個module
,將全部相關的代碼都放在這個module
中。這是第一步。
第二步,爲你的module
提供一個漂亮的門面。好比下載的DownloadManager
, 經緯度的LocationTracker
, 社交分享的SocialManager
。它們作爲功能模塊的入口,要儘可能的簡潔,方法命名好記易理解,類上要有完整的示例註釋
第三步,閉門造車。無論你在裏面幹什麼,外面都是不知道的,就像薛定諤的那隻貓,外層不調用它,永遠不知道它是否好用。
不過爲了之後好維護,不給他人留坑,仍是儘可能寫的工整一些。
裝飾模式跟靜態代理很像。
每次一說裝飾模式,就想成decorator,實際上叫wrapper更直觀些。既然是wrapper,那就得有源的句柄,在構造wrapper時得把source做爲參數傳進來。wrapper了source,一樣還wrapper其餘功能。
代理模式,Proxy Delegate,實際上Delegate也不知道本身被代理了,Proxy假裝成Delegate來執行,既然是proxy,那proxy不該該提供delegate沒有的public方法,以避免被認出來。
拋開理論的描述,咱們直接來看下面的代碼。
你能夠將ExecutorCallbackCall
看成是Wrapper,而真正去執行請求的源Source是OkHttpCall
。之因此要有個Wrapper類,是但願在源Source操做時去作一些額外操做。這裏的操做就是線程轉換,將子線程切換到主線程上去。
簡單的解釋下,enqueue()方法是異步的,也就是說,當你調用
OkHttpCall
的enqueue方法,回調的callback是在子線程中的,若是你但願在主線程接受回調,那須要經過Handler轉換到主線程上去。ExecutorCallbackCall
就是用來幹這個事。固然以上是原生retrofit使用的切換線程方式。若是你用rxjava,那就不會用到這個ExecutorCallbackCall
而是RxJava
的Call了。這裏不展開。
再來講動態代理。以往的動態代理和靜態代理使用的場景是相似的。都想在delegate調用方法先後作一些操做。若是個人代理類有不少方法,那我得額外寫不少代碼,因此這時候就引入了動態代理。經過動態設置delegate,能夠處理不一樣代理的不一樣方法。看不懂不要緊,直接上代碼:
簡而言之,動態代理就是攔截調用的那個方法,在方法先後來作一些操做。Retrofit裏的動態代理比較巧妙。實際上它根本就沒有delegate。由於這個方法沒有真正的實現。使用動態代理,只是單純的爲了拿到這個method上全部的註解。
全部的工做都是由proxy作了。比起咱們總說代理就是打log要高明多了。
我之前本身寫數據庫框架時,也碰到這樣的場景。一個類裏有不少一對一,一對多關係。若是從db裏fetch出來都去作初始化,那會很是影響性能。但若是不初始化,到使用時再去手動初始化就更麻煩了。怎麼辦呢?
class A{ private B b; private ArrayList<C> cs; public B getB(){ return b; } public ArrayList<C> getCs(){ return cs; } }
當類A裏的get方法被invoke時,我就判斷,這個類有沒有被初始化,若是有,那就不作任何操做。
若是沒有,那得等會,我把數據從數據庫中fetch出來給你賦值後,再去invoke。這個場景能夠叫懶加載,能夠套用AOP面向切面編程。
動態代理能實現這個需求嗎?能夠,可是支持的很糟糕。由於動態代理依賴接口實現,總不能將全部的pojo中的方法都申明到接口裏吧?那真是要命了。
因此我用了種替代方案,既然是AOP,有個面向切面的框架AspectJ
。你能夠經過它來切入這些get方法,先判斷有沒初始化,而後再返回。
差很少就是這樣,沒有Proxy的概念,只是在編譯時,把這些切面織入進去。對於pojo而言徹底是透明的。是否是很6。
不過這裏也有不少其餘的性能瓶頸,好比說我在第一次調用時,要先去數據庫fetch,這也是耗時操做。這個先跳過,有機會再跟你們八一八,我那數據庫框架是怎麼擼出來的。
適配器模式就是,已經存在的OkHttpCall
,要被不一樣的標準,平臺來調用。 設計了一個接口CallAdapter
,讓其餘平臺都是作不一樣的實現來轉換,這樣不花很大的代價就能再兼容一個平臺。666。
若是你已經看過retrofit源碼,極可能被CallAdapter
玩壞。這個CallAdapter
不是那麼好理解。先拋開代碼,咱們來看看適配器模式。
Adapter簡單來講,就是將一個已存在的東西轉換成適合咱們使用的東西。就比方說電源Adapter。出國旅遊都要帶轉接頭。比方說,RecyclerView
裏的Adapter是這麼定義的。Adapters provide a binding from an app-specific data set to views。
再回來看看Retrofit,爲何咱們須要轉接頭呢。那個被轉換的是誰?
咱們看看CallAdapter
的定義。Adapts a {@link Call} into the type of {@code T}.
這個Call是OkHttpCall,它不能被咱們直接使用嗎?被轉換後要去實現什麼特殊的功能嗎?
咱們假設下。一開始,retrofit只打算在android上使用,那就經過靜態代理ExecutorCallbackCall
來切換線程。可是後來發現rxjava挺好用啊,這樣就不須要Handler來切換線程了嘛。想要實現,那得轉換一下。將OkHttpCall
轉換成rxjava(Scheduler
)的寫法。再後來又支持了java8(CompletableFuture
)甚至竟然還有iOS支持。大概就是這樣一個套路。固然我相信square的大神確定一開始就考慮了這種狀況,從而設計了CallAdapter
。
在retrofit裏,這個適配器模式不是那麼明顯。並且和其餘模式交錯在一塊兒,因此看起來很麻煩。好比這個CallAdapter又夾雜着策略模式(僅是我的見解)。
你能夠看看Rxjava裏如何去建立adapter的,它是根據api方法聲明的returnType來建立具體的CallAdapter
實例的。上代碼你就明白了。
是否是很像根據不一樣的策略使用不一樣的算法?不一樣的returnType聲明就是set不一樣的Strategy。
來張提綱挈領的流程圖,沒保存的趕忙存起來。之後就能照着它本身開車了。
好,大概就將這麼多啦。這些就是retrofit的解耦套路了。經過一系列的設計模式,封裝思想來解耦,
看到如今,其實retrofit就是一個負責調度的controller。先給retrofit配置好,讓它可以正常工做。你給它一個方法調用,它就在內部開始運轉。這個方法之前我消化過嗎,沒消化那就用一個ServiceMethod來解析它。解析後要用來配置一個request請求。但它本身搞不定這事啊,因此須要給它一個轉接頭,經過轉接頭來使用okhttpcall。請求是作好了,可是response它又不認識,
因此又請來convertor來幫忙,轉換完畢以後才吐出一個咱們最終要的那個對象。
若是看文章不夠過癮,能夠看Stay精心錄製的視頻Retrofit分析-漂亮的解耦套路,看完你不再怕看不懂retrofit了。
並且你還能夠用Stay這種分析套路來輕鬆看懂其餘源碼。
1.Retrofit分析-漂亮的解耦套路
萬萬沒想到Retrofit會這麼火,在沒看源碼以前,我簡單的認爲是由於它跟OkHttp同出一源(Square),因此纔會炒的那麼熱。又或者是由於它能支持RxJava,因此火上澆油,一發不可收拾。
後來看過Retrofit源碼以後,我才理解爲何它倍受關注,是由於它集諸優勢於一身,而且炒雞解耦。你能預見的特殊需求,都能很是容易的擴展。
咱們先來看一下沒有HTTP框架之前,咱們是如何作請求的。
大概是以上4大步驟,在沒有框架的年代,想要作一次請求,是萬分痛苦的,你須要本身管理線程切換,須要本身解析讀取數據,解析數據成對象,切換回主線程,回調給上層。
這段空白的時間持續了好久。從我10年工做起到12年,由於寫煩了重複的代碼,因此就得想辦法,把那些變化的地方封裝起來,也只是簡單的封裝。
好在官方出了AsyncTask,雖然坑不少,但若是再本身維護一個隊列,基本不會出現問題。
更好的地方是數據格式從xml變成json了。gson解放了雙手,不再用解析dom了。
後來慢慢出了很多真正的HTTP框架。Stay也借鑑了不少文章,封裝了一套適用於自身業務需求的框架。
這個時期的框架有個特色,就是拼了命去支持全部類型。比方說Volley支持直接返回Bitmap。xUtils不只大而全,並且連多線程下載也要支持。
在資源匱乏的時代,它們的存在有它們的道理。但若是說如今還用Volley作圖片請求,還在用xUtils或Afinal裏的各個模塊。那就說不過去了。
術業有專攻,百家爭鳴的時期,難道不應選擇最好的那一個嗎?(Stay沒真的用過xUtils和Afinal這種組合框架,潛意識告訴我,它們有毒,一旦某個環節出問題或者須要擴展,那代價就太大了)
tips:本文以retrofit最新版本2.0.1爲例,你們也能夠去github下源碼,找tag爲'parent-2.0.1'就能夠。目前代碼變更比較大。2.0.1已經使用okhttp3了,而我項目中2.0.0-beta2仍是okhttp2.5。
retrofit的最大特色就是解耦,要解耦就須要大量的設計模式,
先來看一張Stay畫的精簡流程圖(若有錯誤,請斧正),類圖就不畫了。
Stay在一些設計模式很明確的地方作了標記。
外觀模式,動態代理,策略模式,觀察者模式。固然還有Builder模式,工廠等這些簡單的我就沒標。
先簡述下流程吧:
經過門面Retrofit來build一個Service Interface的proxy
當你調用這個Service Interface中的某個請求方法,會被proxy攔截。
經過ServiceMethod來解析invoke的那個方法 ,經過解析註解,傳參,將它們封裝成咱們所熟悉的request。而後經過具體的返回值類型,讓以前配置的工廠生成具體的CallAdapter,ResponseConverter,這倆咱們稍後再解釋。
new一個OkHttpCall,這個OkHttpCall算是OkHttp的包裝類,用它跟OkHttp對接,全部OkHttp須要的參數均可以看這個類。固然也仍是能夠擴展一個新的Call的,好比HttpUrlConnectionCall。可是有點耦合。看下圖標註:
紅框中顯式的指明瞭OkHttpCall,而不是經過工廠來生成Call。因此若是你不想改源碼,從新編譯,那你就只能使用OkHttp了。不過這不礙事。(可能也是由於還在持續更新中,因此這塊可能後面會改進的)
生成的CallAdapter有四個工廠,分別對應不一樣的平臺,RxJava, Java8, Guava還有一個Retrofit默認的。這個CallAdapter不太好用中文解釋。 簡單來講就是用來將Call轉成T的一個策略。由於這裏具體請求是耗時操做,因此你須要CallAdapter去管理線程。怎麼管理,繼續往下看。
好比RxJava會根據調用方法的返回值,如Response<'T> |Result<'T>|Observable<'T> ,生成不一樣的CallAdapter。實際上就是對RxJava的回調方式作封裝。好比將response再拆解爲success和error等。(這塊仍是須要在瞭解RxJava的基礎上去理解,之後有時間能夠再詳細作分析)
在步驟5中,咱們說CallAdapter還管理線程。比方說RxJava,咱們知道,它最大的優勢能夠指定方法在什麼線程下執行。如圖
在adapt Call中,具體的調用了Call execute(),execute()是同步的,enqueue()是異步的。由於RxJava已經切換了線程,因此這裏用同步方法execute()。
接下來的具體請求,就是OkHttp的事情了,retrofit要作成的就是等待返回值。在步驟4中,咱們說OkHttpCall是OkHttp的包裝類,因此將OkHttp的response轉換成咱們要的T,也是在OkHttpCall中執行的。
固然具體的解析轉換操做也不是OkHttpCall來作的,由於它也不知道數據格式是什麼樣的。因此它只是將response包裝成retrofit標準下的response。
Converter->ResponseConverter,很明顯,它是數據轉換器。它將response轉換成咱們具體想要的T。Retrofit提供了不少converter factory。好比Gson,Jackson,xml,protobuff等等。你須要什麼,就配置什麼工廠。在Service方法上聲明泛型具體類型就能夠了。
最後,經過聲明的observeOn線程回調給上層。這樣上層就拿到了最終結果。至於結果再如何處理,那就是上層的事了。
再來回顧下Stay畫的流程圖:
這真是漫長的旅行,Stay也是debug一個個單步調試才梳理出來的流程。固然其中還有不少巧妙的解耦方式,我這裏就不贅述了。你們能夠看看源碼分析下,當真是設計模式的經典示例。
我想如今你們應該對retrofit有所瞭解了。當你再給別人介紹retrofit的時候,就別隻說它的註解方式多新穎,多炫技了。註解式框架有不少的,像j2ee中一大把。因此註解算不得多精湛的技藝。 最牛逼的仍是它的解耦方式,這個套路沒有多年的實際架構經驗是設計不出來的。