spring-cloud-square原始碼速讀(spring-cloud-square-okhttp篇)

程式設計師欣宸發表於2021-11-02

歡迎訪問我的GitHub

https://github.com/zq2599/blog_demos

內容:所有原創文章分類彙總及配套原始碼,涉及Java、Docker、Kubernetes、DevOPS等;

前文回顧

  • 本篇是欣宸《spring-cloud-square原創》系列的第三篇,我們們快速回顧一下前面兩篇:
  1. 《五分鐘搞懂spring-cloud-square》:說清楚了spring-cloud-square是什麼
  2. 《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開啟原始碼,得到的專案結構如下:

在這裡插入圖片描述

  • 今天,我們們的目標就是上圖的<font color="blue">spring-cloud-square-okhttp</font>子工程,讀它原始碼,學它精髓!

提前小結

  • 總所周知,欣宸文筆水平在CSDN墊底,還喜歡廢話,導致很多讀者都看不下去,因此這裡提前做個小結,將本篇精華直接奉上,如果您時間有限,或者乾脆沒興趣深入瞭解,可以看完小結後直接離開,也不算毫無收穫.....
  • 將整個工程原始碼串起來小結:
  1. spring-cloud-square-okhttp.jar的使用者是個java應用,該應用要寫程式碼例項化OkHttpClient.Builder物件
  2. spring.factories中配置的OkHttpLoadBalancerAutoConfiguration,會被spring框架掃到並例項化
  3. OkHttpLoadBalancerAutoConfiguration中例項化了OkHttpLoadBalancerInterceptor,並將LoadBalancerClient例項傳給它
  4. OkHttpLoadBalancerAutoConfiguration中例項化了OkHttpClientBuilderCustomizer介面的實現,這裡面是個lambda表示式,功能是將所有Interceptor傳給lambda表示式對應的builder
  5. OkHttpLoadBalancerAutoConfiguration中例項化了OkHttpBuilderBeanPostProcessor,當步驟1中的OkHttpClient.Builder物件被例項化後,OkHttpBuilderBeanPostProcessor會呼叫OkHttpClient.Builder的addInterceptor物件,將OkHttpLoadBalancerInterceptor傳給OkHttpClient.Builder
  6. 業務程式碼遠端訪問的時候,用OkHttpClient.Builder建立OkHttpClient物件,此時的OkHttpClient就得到了OkHttpLoadBalancerInterceptor,在遠端訪問時,業務程式碼傳入的URL中是遠端服務的名字,但是OkHttpLoadBalancerInterceptor會藉助LoadBalancerClient將遠端服務的名字替換成對應的IP和埠,然後再執行真正的網路請求
  • 聽說一圖勝千言,欣宸二把刀的作圖技術實在不敢恭維,但還是堅持把重要步驟用圖表達出來了,如下所示,希望您能將就著看:

在這裡插入圖片描述

  • 接下來,如果您還想深入研究和了解spring-cloud-square,就隨欣宸一起暢遊它的原始碼世界吧

知識點補充(OkHttpClient.Builder.addInterceptor)

  • 首先要補充一個重要知識點:OkHttpClient.Builder.addInterceptor方法的作用是什麼?
  • 看原始碼很簡單,就是將interceptor放入集合<font color="blue">interceptors</font>中:
    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為例,下圖紅框中的方法<font color="red">極為重要</font>,這行程式碼執行後,會回到上一幅圖中的proceed方法,繼續處理下一個interceptor:

在這裡插入圖片描述

  • 至此可以小結了:OkHttpClient.Builder.addInterceptor方法的作用,是傳入一個Interceptor實現類,在OkHttpClient執行網路請求的時候,該Interceptor的intercept方法會被執行,請記住這個小結,後面有大用處!

spring-cloud-square-okhttp

  • spring-cloud-square提供了三種具體的實現,第一種是<font color="blue">spring-cloud-loadbalancer + spring-cloud-square-okhttp</font>的組合,而spring-cloud-loadbalancer是另一個專案不在此文中展開,因此,我們們最先看的就是<font color="red">spring-cloud-square-okhttp</font>的原始碼了
  • 開啟專案如下圖,我只能感慨兩個字:<font color="blue">就這?</font>

在這裡插入圖片描述

  • 關於配置檔案additional-spring-configuration-metadata.json,在spring文件中有提到,如下圖紅框,負責處理註解的處理器會將additional-spring-configuration-metadata.json的內容合併到後設資料檔案中去:

在這裡插入圖片描述

  • 看看additional-spring-configuration-metadata.json的內容,如下,定義了屬性<font color="blue">okhttp.loadbalancer.enabled</font>的預設值為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和地址,功能僅此而已,但是收穫是否會止步於此呢?這是個主觀問題,各人的收穫都不一樣吧,我這最大的收穫有以下兩點:
  1. OkHttpClient的鏈式處理很精彩,Interceptor.intercept中強制要求執行chain.proceed方法,讓我想起了裝飾者模式中的疊加處理邏輯
  2. 如何用<font color="blue">spring.factories + AutoConfig + BeanPostProcessor + SpringCloud LoadBalance</font>協同作戰,spring-cloud-square-okhttp算是給我上了一課,尤其是OkHttpLoadBalancerAutoConfiguration中三個構造器的順序注入,讓人有種鼓掌叫好的衝動,我能寫出這樣簡潔明快的starter嗎?
  3. 至此,spring-cloud-square原始碼速讀的spring-cloud-squarespring-cloud-square-okhttp篇已經完成了,在您學習spring的道路上,希望本文能夠帶給您一些參考
  4. 接下來要挑戰的是spring-cloud-square的retrofit相關原始碼,程式碼量會增加很多,但是,何懼之有?欣宸原創,必不會辜負您的期待!

你不孤單,欣宸原創一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 資料庫+中介軟體系列
  6. DevOps系列

歡迎關注公眾號:程式設計師欣宸

微信搜尋「程式設計師欣宸」,我是欣宸,期待與您一同暢遊Java世界..
https://github.com/zq2599/blog_demos

相關文章