一、爲何我決定寫Spring Cloud專欄

題語:學習方法之多寫:模仿、比較、超越
作者:A哥(YourBatman)
wx號:fsx641385712(備註“Java羣”字樣)
公衆號:BAT的烏托邦(ID:BAT-utopia)

代碼下載地址:https://github.com/f641385712/spring-cloud-learning

前言

各位小夥伴大家好,我是A哥。瞭解我文章的小夥伴應該知道,到目前爲止A哥還幾乎沒有寫過Spring Boot/Spring Cloud的文章,雖然寫了不少關於Spring Framework的內容,但仍舊被不少小夥伴認爲與時代脫節。都什麼年代了,誰還會直接使用Spring Framework呢?是的沒毛病,那麼至於爲何我一直堅持Spring Framework纔是最重要的呢,請看下圖:

Spring和Spring Boot
你對Spring Framework的瞭解程度決定了你對Spring Boot的瞭解程度,你對Spring Boot的瞭解程度決定了你對Spring Cloud的瞭解程度。


爲何我決定寫Spring Cloud專欄?

A哥一直遲遲不去寫Spring Cloud系列,主要是“市面上”的Spring Boot/Spring Cloud相關文章實在太多了,感覺這湯飯被炒得太多次了,被玩壞了的趕腳,所以是有畏懼的。經過一段時間的思考,最終A哥還是決定要去“冒險”一波,主要有如下兩方面原因促成:

  1. 通過小夥伴們的反饋,對Spring Cloud體系的學習確實有不小的訴求。這很容易理解:誰讓它這麼火呢~
  2. “市面上”的SB/SC系列教程,大都偏使用層面,站在使用者的角度去分析。讀完了可能會Run了,可成爲一個合格的API調用工程師,但背後的緣由、對於如何擴展仍舉步維艱

還記得我剛入門的時我“師父”對我說過一段話,結合現在語境翻譯爲:如果你對SB/SC不甚瞭解,並且只是抱着一種just run的態度去對待,那你的工作會很累且難有積累和提升。不知道爲何能run和爲何不能run,做起事來心裏必然會有所顧慮。Spring Boot/Spring Cloud對初學者非常的友好 ,並且兼具了極大的靈活性,因此不管是對於新手還是老手它都具有很大的學習以及可擴展、可定製空間。

熟悉A哥的小夥伴都知道,我雖然騷氣,但並不會擅長寫抓人眼球文章,反而更願意沉下心去寫些更爲實用的文章,不求訪問量,只求產生共鳴。那麼爲何大多數人都願意寫水文(記錄自己生活、嘮嗑等文),而不願意寫技術文呢?主要有如下兩大原因:

  1. 技術太過於枯燥,很少人願意靜心閱讀
  2. 技術文一般很難單獨成篇,它對上下文(特別是上文)是有依賴的,這也是爲何很多人讀不下去的原因
  3. 業內共識:水文寫一篇僅需1到2小時,而一篇還不錯的技術文往往要花1-2天甚至更多的時間精力

技術文的閱讀量一般遠遠不如“同級別”的水文。有點費力不討好之嫌。利益驅使,因此堅持這麼做的是比較少的,我也不知自己還能堅持幾天~

在大環境非常不樂觀的當下,唯有技術過硬是你的立身之本,大潮退去才知道誰在裸泳,這對真正的技術人或許是機遇勒。那麼來吧,跟着A哥一起的修行之路從這啓程,期待本系列做得有血、有肉、有內容、有深度…

學習去...


正文

何爲Spring Cloud?摘抄自官網:Spring Cloud爲開發人員提供了工具,以快速構建分佈式系統中的一些常見模式(例如,配置管理,服務發現,斷路器,智能路由,微代理,控制總線)。分佈式系統的協調導致樣板式樣,並且使用Spring Cloud開發人員可以快速站起來實現這些樣板的服務和應用程序。它們可以在任何分佈式環境中正常工作,包括開發人員自己的筆記本電腦,裸機數據中心以及Cloud Foundry等託管平臺。

A哥看到很多SC系列開門見山是教你如何去搭建應用,把SC跑起來。但作爲只寫“高逼格”文章的我自然不會從哪聊起嘍,也費你時間不是。本系列會認爲你已經對Spring Boot有一定的瞭解,並且基本能夠正常使用SC了,這樣效果最佳。

關於Spring Boot我並無打算寫它,但我寫了一篇“總結篇”,請移步此處參閱:不懂SpringApplication生命週期事件體系?那就等於不會Spring Boot嘛

本文單刀直入,上來就是源碼分析以及對應實例講解,嘎嘎就是幹,一把嗦。所以我們學習Spring Cloud那必然先來到它的核心中的核心:Spring Cloud Context


版本約定

不約定版本的專題講解是不負責任的,特別對於這麼“浪”的SC來說尤甚(畢竟不向下兼容在它身上太常見了)。版本上爲了和A哥的其它專題系列保持一致方便後續串講,本文對相關庫的版本做出如下約定:

  • Spring Boot版本:2.2.2.RELEASE
  • Spring Cloud版本:Hoxton.SR1
    • Spring Boot版本:2.2.2.RELEASE
    • spring-cloud-starter-xxx版本:2.2.1.RELEASE

說明:你閱讀到本文時Spring Cloud版本可能已經到了Hoxton.SR4,Spring Boot版本也可能已經到了2.2.6.RELEASE。但是像這種專欄式的學習,你完全可忽略小版本之間的差異,姑且認爲他們是一樣的即可

工程pom文件的大致內容如下:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath/>
</parent>

...

<properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>

...

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

關於Spring Cloud Context

也許你從沒聽過SC的Context上下文概念,或者從沒聽過spring-cloud-context這個工程,這都是非常正常的,畢竟實際Just Run條件下確實無需知曉,對初學者非常友好嘛,這是必須做到的。但你應該在pom裏見過or導入過spring-cloud-starter這個jar:

在這裏插入圖片描述

  • spring-cloud-context:SC的context上下文、配置、生命週期管理、健康檢查等等
  • spring-cloud-commons:大名鼎鼎的SC的commons抽象。含有如:服務註冊/發現、熔斷器、負載均衡等公共抽象,面向該抽象編程可以無需關心底層實現,帶來一致的編程感受,這是Spring家族最擅長乾的事

很明顯,本系列將把它拆散開來逐個解釋其作用,目的是深入理解,從本質上感受SC的設計以及它的抽象方式和實現方式。那麼首先就來到spring-cloud-context這個工程嘍。

GAV如下:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-context</artifactId>
</dependency>

在這裏插入圖片描述

說明:實際生產中,此jar你一般不會單獨導入,而是伴隨着Spring Cloud starter一起帶入的。但“講課”就得基於最小化原則進行講解嘛,因此我需要最小化的依賴,我的工程裏只導入此jar


本jar的spring.factories文件解釋

這個jar很“單純”,並不拖泥帶水,它裏面存在一個spring.factories文件,內容如下:

到這裏了可別問spring.factories是什麼?有什麼作用之類的話了哈。它是Spring SPI機制(並非Spring Boot提供的功能喲)的實現,具體參考這個類SpringFactoriesLoader

# AutoConfiguration 自動配置類(一般情況下只有SB容器去執行它,但是不是確定的)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.WritableEnvironmentEndpointAutoConfiguration


# Application Listeners 監聽器們
org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\  -> 監聽ApplicationEnvironmentPreparedEvent事件
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\ -> 監聽ApplicationEnvironmentPreparedEvent事件
org.springframework.cloud.context.restart.RestartListener -> 監聽ApplicationPreparedEvent/ContextRefreshedEvent事件


# Bootstrap components 由Spring Cloud容器負責加載的配置類們
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration

此配置文件內容一共分爲三個部分,會涉及到執行順序的問題:

再次有必要強調一次,建議你已經掌握了此文的知識效果更佳:不懂SpringApplication生命週期事件體系?那就等於不會Spring Boot嘛

  1. 自動配置類。有的小夥伴認爲自動配置類只能是Spring Boot容器纔去執行它,其實這是不對的。確切的說:只要容器內開啓了自動配置(有Bean標註了@EnableAutoConfiguration註解,實際是向容器導入了AutoConfigurationImportSelector)就都會執行執行spring.factories裏的自動配置類
    1. Spring Boot自然不用說,@SpringBootApplication裏就默認開啓了自動配置
    2. Spring Cloud裏,雖然也使用SpringApplication去啓動容器,但是因爲SC容器並沒有開啓自動配置,所以它不會執行
    3. 強調說明:自動配置的執行時機是refresh()容器啓動時(執行AutoConfigurationImportSelector#selectImports)。另外,如果你的容器並不需要開啓自動配置(就像SC一樣),請不要開啓,挺耗時的(前提是你對SB、SC有足夠了解,不然容易出bug)。或者你也可以使用ImportAutoConfigurationImportSelector來手動接管
  2. 監聽器。如上標註,這幾個監聽器都是監聽的SpringApplication的生命週期事件,因此它們在SB的容器啓動時的不同生命週期裏會按順序執行,特別是最重要的BootstrapApplicationListener它負責Spring Cloud容器的啓動工作
    1. 監聽器是由SpringApplication應用去加載的,而SC也是由SpringApplication去啓動的,所以SC也會讓這些監聽器挨個執行哦,若有必要,請注意“防範”
  3. Spring Cloud的啓動/引導配置。這些配置是SC專屬,詳見下面的說明

Spring Cloud專屬配置類

也叫Bootstrap引導容器的專屬配置類。通過上面3步的第一步解釋可知:Spring Cloud它並不會去執行/加載你配置的EnableAutoConfiguration自動配置類,那麼如果SC它在引導期間需要屬於自己的配置怎麼辦呢???

這就到了SC它專爲自己開發出來的一套配置機制,姑且把它叫做Spring Cloud專屬配置類。不同於Spring Boot自動配置使用的是org.springframework.boot.autoconfigure.EnableAutoConfiguration,它作爲SC專屬的,自然key也就不能一樣嘍,使用的是:org.springframework.cloud.bootstrap.BootstrapConfiguration@BootstrapConfiguration註解的全類名)。通過這個key配置的配置類們(可以是普通配置類,也可以是自動配置類),就是隻能被SC容器在啓動時加載的專屬配置類,是SC父容器專屬。

SB的自動配置類使用AutoConfigurationImportSelector負載最後加載,此處SC的專屬配置類使用的是BootstrapImportSelector負責加載,它倆的共同點是:都是一個DeferredImportSelector,所以執行時機是最晚的

通過以上的瞭解,我們可以知道,SC的專屬配置類有如下幾個特點:

  1. 該配置只能被Spring Cloud容器/Bootstrap引導上下文讀取/加載
  2. 該配置的加載時機最早:優先於SB的普通配置、自動配置之前加載,所以它是具有最高優先級的配置類
  3. 該配置存在於父容器(SC容器)內,而非SB容器內
    1. 當然嘍,SB容器也可以訪問

Spring Boot容器 vs Spring Cloud容器

我們知道,Spring Cloud容器是Spring Boot容器的父容器。爲了讓你更直觀的看到父子容器內的Bean情況(個數 + 詳情),瞭解其區別,此處A哥寫個最簡案例比較一波:

public static void main(String[] args) {
    ConfigurableApplicationContext bootContext = SpringApplication.run(Application.class, args);
    System.out.println("boot容器類型" + bootContext.getClass());
    System.out.println("boot容器Bean定義總數:" + bootContext.getBeanFactory().getBeanDefinitionCount());
    System.out.println("boot容器Bean實例總數:" + bootContext.getBeanFactory().getSingletonCount());

    ConfigurableApplicationContext cloudContext = (ConfigurableApplicationContext) bootContext.getParent();
    System.out.println("cloud容器類型" + cloudContext.getClass());
    System.out.println("cloud容器Bean定義總數:" + cloudContext.getBeanFactory().getBeanDefinitionCount());
    System.out.println("cloud容器Bean實例總數:" + cloudContext.getBeanFactory().getSingletonCount());
}

控制檯輸出:

boot容器類型class org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
boot容器Bean定義總數:272
boot容器Bean實例總數:283

cloud容器類型class org.springframework.context.annotation.AnnotationConfigApplicationContext
cloud容器Bean定義總數:22
cloud容器Bean實例總數:34

SC容器(父容器)裏的“內容”是遠遠少於SB容器(子容器的)。由於父容器裏內容較少,可以詳細看看。A哥這裏幫你截圖讓你更直觀的感受一把嘍:

context.getBeanFactory().getBeanDefinitionNames()(共22個):

在這裏插入圖片描述
context.getBeanFactory().getSingletonNames()(共34個):

在這裏插入圖片描述
看完了這個結果,相信你有個感官的認識了。那麼A哥給小夥伴們提3個小問題哈,你可以自行思考:

  1. 爲何一個是34,一個是22,這兩個值爲毛不等呢?差異在哪兒呢?
  2. SB容器爲毛只是單單啓動完,就有這麼多Bean了呢?爲何它如此重呢?
  3. SB應用有幾個context上下文?SC應用呢?

總結

關於Spring Cloud系列的第一篇內容就寫到這嘍。本文先解釋了爲何我要寫這個系列,以及簡單的介紹了下Spring Cloud Context工程,通過本文你是能夠大概清楚A哥後面將如何展開的,比如下文將是本系列的主菜之一:BootstrapApplicationListener監聽器負責Spring Cloud容器的引導/啓動/初始化,敬請關注。

分隔線

關注我

本號所有“享學xxx”系列文章/視頻,和什麼“享學課堂”、“享學科技”無任何關係。只因筆者名字和其重名,僅此而已

  • 原創不易,碼字更不易,請勿白嫖,你的三連是對A哥的最大支持
  • 關注我吧:一個前25年還不會寫Hallo World的半殘程序猿,中文名:A哥,英文名:YourBatman

在這裏插入圖片描述
往期精選

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章