歡迎訪問我的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