最近開始在GitHub上找一些優秀的開源項目,跟團隊一塊兒閱讀源代碼,每週一次,每次一個半小時左右,美其名曰「賞碼會」(還記得《唐伯虎點秋香》那句「賞花賞月賞秋香」嗎?)。爲何要閱讀源代碼?好處舉不勝舉,好比學習如何合理的命名,如何寫出簡潔、清晰的註釋,如何編寫有效的單元測試,知道良好的編碼風格是什麼樣的。有一些積累以後,能夠試試看找一找隱藏在代碼裏的設計模式,加一些新的單元測試,想想若是本身實現會如何設計。閱讀源代碼能夠說是有百利而無一害,屬於典型的第二象限的事(參見《高效能人士的七個習慣》)。html
第一次「賞碼會」我選的是Retrofit項目,爲啥選它呢?第一,小巧(核心代碼不到5000行),第二,高Star(17+K),第三,平時一直在用。先簡單介紹一下Retrofit這個框架。Retrofit是Square公司開源的一個Java實現的輕量級HTTP Client框架,本質上是對Square公司另外一個開源框架OkHTTP的一層type-safe的封裝。所謂的type-safe,個人理解就是將OkHTTP原生的Request/Response對象經過類型安全的方式轉化爲其餘任意類型的對象,好比String,用戶自定義類型等。java
面向接口的聲明式API定義風格是Retrofit最受歡迎的特性,例以下面的GitHubService接口的listRepos方法定義了GitHub的List user repositories API。git
public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); }
無需定義具體的實現類,就能夠直接調用,例如:程序員
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); GitHubService service = retrofit.create(GitHubService.class); Call<List<Repo>> repos = service.listRepos("octocat");
有經驗的Java程序員馬上就能看出,相對於其餘的HTTP Client框架,好比Apache HttpClient或者Async Http Client,使用Retrofit編程效率將產生質的提高。github
從GitHub拉取Retrofit的源代碼,導入retrofit子工程,核心代碼都在retrofit包下。apache
核心類列舉以下:編程
Retrofit: Retrofit框架的門面類,大多數狀況下,你的代碼中只須要用到它。設計模式
ServiceMethod: 對應接口類中的一個方法(好比上文中的listRepos),負責解析方法簽名中用到的各類註解,生成最終的Request對象。api
Call: 相似於Java 8裏面的CompletableFuture,提供異步支持。安全
CallAdapter: Retrofit默認只接受Call<?>做爲方法返回類型,若是須要使用其餘類型,就要添加額外的CallAdapter。
Converter: Retrofit默認只接受Response和Void做爲Call<?>的類型參數,若是須要使用其餘類型,就要添加額外的Converter。
和任何形式的閱讀(讀書,讀人,讀心)同樣,要讀懂源代碼,必定要帶着問題去讀。爲了幫助理解上述幾個核心類的關係,簡單列舉幾個我閱讀代碼時思考的問題,
答案很簡單,由於使用了JDK的動態代理,很是討巧的設計。
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(); @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); } }); }
剛纔已經提到了,經過Retrofit.Builder#addCallAdapterFactory()添加相應的CallAdapter,例如想返回CompleteableFuture,可使用Retrofit提供的Java8CallAdapterFactory。
跟動態設置Headers相似,能夠自定義用於打印日誌的OkHttp interceptor,而後添加到本身建立的OkHttpClient實例,再綁定Retrofit.Builder#client()。
總的來講,Retrofit框架設計精巧,上手簡單,開發效率高,但也存在一些不足。第一,跟OkHTTP框架綁定太死,不像Feign那麼靈活,支持多種Client。第二,和JDK的動態代理強綁定,對其餘AOP方式不友好,好比這個Issue提到的Hystrix集成問題。
時間有限,先寫到這裏,將來我會不按期放一些「賞碼會」的心得,歡迎到個人GitHub留言交流。