歡迎訪問我的GitHub
https://github.com/zq2599/blog_demos
內容:所有原創文章分類彙總及配套原始碼,涉及Java、Docker、Kubernetes、DevOPS等;
前文回顧
- 本篇是欣宸《spring-cloud-square原創》系列的第三篇,我們們快速回顧一下前面兩篇:
- 《五分鐘搞懂spring-cloud-square》:說清楚了spring-cloud-square是什麼
- 《spring-cloud-square開發實戰(三種型別全覆蓋)》:說清楚了spring-cloud-square怎麼用
- 接下來,為了深入瞭解spring-cloud-square,我打算讀它的原始碼,愛學習的您有沒有興趣一起呢?
閱讀原始碼的動機
- 先說一下為什麼要去看spring-cloud-square原始碼
- spring是java開源專案的典範,它的原始碼具備很高的質量和參考價值
- 和其他spring專案相比,spring-cloud-square的原始碼少得可憐,花最少的時間閱讀spring專案的原始碼,這事兒我挺有興趣
- 自動裝配、攔截器、業務介面自動實現,這些技術都出現在spring-cloud-square專案中,看懂了學會了,對自己的專案有很高的參考價值
把握核心方向
- 在spring-cloud-square這種整合了多個庫的專案中,涉及的原始碼是很多的,很容易陷入一個又一個程式碼的細節中(不停的展開涉及的類和原始碼),因此,這裡先確定本次原始碼分析的核心方向:
- okhttp原始碼,只看關鍵部分,其餘一律跳過
- 只想知道在使用spring-cloud-square-okhttp.jar的時候,為何輸入服務名就能訪問到對應的服務
- 以上就是這次閱讀原始碼的主方向,在陷入細節時,這個主方向會及時將我拉回來,繼續朝既定目標前進
下載原始碼
- 下載完畢後解壓,用IDEA開啟原始碼,得到的專案結構如下:
- 今天,我們們的目標就是上圖的spring-cloud-square-okhttp子工程,讀它原始碼,學它精髓!
提前小結
-
總所周知,欣宸文筆水平在CSDN墊底,還喜歡廢話,導致很多讀者都看不下去,因此這裡提前做個小結,將本篇精華直接奉上,如果您時間有限,或者乾脆沒興趣深入瞭解,可以看完小結後直接離開,也不算毫無收穫.....
-
將整個工程原始碼串起來小結:
- spring-cloud-square-okhttp.jar的使用者是個java應用,該應用要寫程式碼例項化OkHttpClient.Builder物件
- spring.factories中配置的OkHttpLoadBalancerAutoConfiguration,會被spring框架掃到並例項化
- OkHttpLoadBalancerAutoConfiguration中例項化了OkHttpLoadBalancerInterceptor,並將LoadBalancerClient例項傳給它
- OkHttpLoadBalancerAutoConfiguration中例項化了OkHttpClientBuilderCustomizer介面的實現,這裡面是個lambda表示式,功能是將所有Interceptor傳給lambda表示式對應的builder
- OkHttpLoadBalancerAutoConfiguration中例項化了OkHttpBuilderBeanPostProcessor,當步驟1中的OkHttpClient.Builder物件被例項化後,OkHttpBuilderBeanPostProcessor會呼叫OkHttpClient.Builder的addInterceptor物件,將OkHttpLoadBalancerInterceptor傳給OkHttpClient.Builder
- 業務程式碼遠端訪問的時候,用OkHttpClient.Builder建立OkHttpClient物件,此時的OkHttpClient就得到了OkHttpLoadBalancerInterceptor,在遠端訪問時,業務程式碼傳入的URL中是遠端服務的名字,但是OkHttpLoadBalancerInterceptor會藉助LoadBalancerClient將遠端服務的名字替換成對應的IP和埠,然後再執行真正的網路請求
- 聽說一圖勝千言,欣宸二把刀的作圖技術實在不敢恭維,但還是堅持把重要步驟用圖表達出來了,如下所示,希望您能將就著看:
- 接下來,如果您還想深入研究和了解spring-cloud-square,就隨欣宸一起暢遊它的原始碼世界吧
知識點補充(OkHttpClient.Builder.addInterceptor)
-
首先要補充一個重要知識點:OkHttpClient.Builder.addInterceptor方法的作用是什麼?
-
看原始碼很簡單,就是將interceptor放入集合interceptors中:
public Builder addInterceptor(Interceptor interceptor) {
if (interceptor == null) throw new IllegalArgumentException("interceptor == null");
interceptors.add(interceptor);
return this;
}
- builder是為例項化OkHttpClient服務的,去看OkHttpClient的構造方法,發現interceptors被複制過來了:
- 在使用OkHttpClient訪問網路的時候,會執行下圖紅框中的getResponseWithInterceptorChain:
- 然後就是經典的鏈式處理了,所有的interceptor都會被執行,下圖展示瞭如何構造和啟動鏈式處理:
- 進入proceed內部,可見每次執行proceed方法,都會取出一個interceptor,呼叫其intercept方法:
- 以spring-cloud-square框架的OkHttpLoadBalancerInterceptor為例,下圖紅框中的方法極為重要,這行程式碼執行後,會回到上一幅圖中的proceed方法,繼續處理下一個interceptor:
- 至此可以小結了:OkHttpClient.Builder.addInterceptor方法的作用,是傳入一個Interceptor實現類,在OkHttpClient執行網路請求的時候,該Interceptor的intercept方法會被執行,請記住這個小結,後面有大用處!
spring-cloud-square-okhttp
-
spring-cloud-square提供了三種具體的實現,第一種是spring-cloud-loadbalancer + spring-cloud-square-okhttp的組合,而spring-cloud-loadbalancer是另一個專案不在此文中展開,因此,我們們最先看的就是spring-cloud-square-okhttp的原始碼了
-
開啟專案如下圖,我只能感慨兩個字:就這?
- 關於配置檔案additional-spring-configuration-metadata.json,在spring文件中有提到,如下圖紅框,負責處理註解的處理器會將additional-spring-configuration-metadata.json的內容合併到後設資料檔案中去:
- 看看additional-spring-configuration-metadata.json的內容,如下,定義了屬性okhttp.loadbalancer.enabled的預設值為true:
{
"groups": [
],
"properties": [
{
"name": "okhttp.loadbalancer.enabled",
"type": "java.lang.Boolean",
"description": "Allows disabling OkHttp Spring Cloud LoadBalancer support.",
"defaultValue": "true"
}
]
}
- 如果您寫過自定義starter庫,那麼您一定知道,整個spring-cloud-square-okhttp專案應該從spring.factories檔案看起,這裡面會確定那些配置類要被例項化:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.square.okhttp.loadbalancer.OkHttpLoadBalancerAutoConfiguration
- 可見配置類OkHttpLoadBalancerAutoConfiguration會被例項化,我們們去看看OkHttpLoadBalancerAutoConfiguration.java,如下圖,經過一長串分析得到一個結論:OkHttpBuilderBeanPostProcessor被例項化了
- 再看OkHttpBuilderBeanPostProcessor.java,如下圖,重點關注紅框中的三個關鍵點:
- 回憶《spring-cloud-square開發實戰(三種型別全覆蓋)》中的程式碼,我們們在使用spring-cloud-square-okhttp.jar的時候,要自己寫一個配置類來例項化OkHttpClient.Builder,如下所示,因此可見:OkHttpBuilderBeanPostProcessor就是給我們們建立的OkHttpClient.Builder例項準備的,簡單的說,就是OkHttpClient.Builder在建立後,就有OkHttpBuilderBeanPostProcessor將OkHttpLoadBalancerInterceptor傳遞給OkHttpClient.Builder:
package com.bolingcavalry.consumer;
import okhttp3.OkHttpClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class OkHttpClientConfig{
@Bean
@LoadBalanced
public OkHttpClient.Builder okHttpClientBuilder() {
return new OkHttpClient.Builder();
}
}
-
此刻的您有沒有恍然大悟呢?原來如此啊,所謂spring-cloud-square-okhttp,其實就是要求使用者自己做一個OkHttpClient.Builder例項,然後spring-cloud-square-okhttp負責將OkHttpLoadBalancerInterceptor塞給OkHttpClient.Builder例項,如此一來,我們在使用OkHttpClient做遠端呼叫的時候,OkHttpLoadBalancerInterceptor的intercept方法就會被執行了!
-
最後要看的就是OkHttpLoadBalancerInterceptor了,其實聰明的您此刻已經猜到它的作用了,它持有了LoadBalancerClient例項,那麼在訪問網路的時候,就可以將URL中的服務名摳出來,用LoadBalancerClient查到對應的服務地址,然後OkHttpClient遠端訪問可以用這個地址了,沒錯,就是如此:
收穫
- 其實整個原始碼的核心就是給OkHttpClient塞進去一個Interceptor,這個Interceptor可以將服務名替換成IP和地址,功能僅此而已,但是收穫是否會止步於此呢?這是個主觀問題,各人的收穫都不一樣吧,我這最大的收穫有以下兩點:
- OkHttpClient的鏈式處理很精彩,Interceptor.intercept中強制要求執行chain.proceed方法,讓我想起了裝飾者模式中的疊加處理邏輯
- 如何用spring.factories + AutoConfig + BeanPostProcessor + SpringCloud LoadBalance協同作戰,spring-cloud-square-okhttp算是給我上了一課,尤其是OkHttpLoadBalancerAutoConfiguration中三個構造器的順序注入,讓人有種鼓掌叫好的衝動,我能寫出這樣簡潔明快的starter嗎?
- 至此,spring-cloud-square原始碼速讀的spring-cloud-squarespring-cloud-square-okhttp篇已經完成了,在您學習spring的道路上,希望本文能夠帶給您一些參考
- 接下來要挑戰的是spring-cloud-square的retrofit相關原始碼,程式碼量會增加很多,但是,何懼之有?欣宸原創,必不會辜負您的期待!
你不孤單,欣宸原創一路相伴
歡迎關注公眾號:程式設計師欣宸
微信搜尋「程式設計師欣宸」,我是欣宸,期待與您一同暢遊Java世界..
https://github.com/zq2599/blog_demos