0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

初學(xué)者必看的SpringBoo自動(dòng)裝配原理4

jf_78858299 ? 來(lái)源:CSDN CC_且聽(tīng)風(fēng)吟 ? 作者:CC_且聽(tīng)風(fēng)吟 ? 2023-04-07 11:03 ? 次閱讀

7. 自動(dòng)裝配源碼分析

終于來(lái)到了大家喜聞樂(lè)見(jiàn)的部分:源碼分析

在我們前面6節(jié)學(xué)習(xí)了各種”招式“之后,讓我們請(qǐng)出對(duì)手:SpringBoot

現(xiàn)在在你面前的是一個(gè)SpringBoot”空項(xiàng)目“,沒(méi)有添加任何依賴包和starter包

啟動(dòng)項(xiàng)目:

正常啟動(dòng),讓我們從@SpringBootApplication開始研究

7.1 @SpringBootConfiguration

會(huì)看到@SpringBootApplication這個(gè)注解由好多注解組成

主要的有以下三個(gè):

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

先來(lái)看第一個(gè):@SpringBootConfiguration

進(jìn)入這個(gè)注解之后會(huì)發(fā)現(xiàn)

原來(lái)你就是一個(gè)@Configuration啊,一個(gè)JavaConfig配置類

那我們使用JavaConfig不就是用來(lái)配置bean嗎,所以有了這個(gè)注解之后我們可以在SpringBoot運(yùn)行的主類中使用@Bean標(biāo)簽配置類了,如下圖所示:

7.2 @ComponentScan

這個(gè)注解相信大家都認(rèn)識(shí)了,組件掃描

這個(gè)掃描的范圍是:SpringBoot主啟動(dòng)類的同級(jí)路徑及子路徑

7.3 @EnableAutoConfiguration

來(lái)看這個(gè)注解,也是最核心的內(nèi)容

這個(gè)注解怎么這么眼熟啊,還記得剛才的@MyEnableAutoConfig注解嗎?就是我們自己寫的那個(gè)注解

進(jìn)入@EnableAutoConfiguration:

看圖中紅圈位置的注解:@Import(AutoConfigurationImportSelector.class)

是不是跟我們上面自己寫的內(nèi)容一樣!

這里的作用便是導(dǎo)入了 AutoConfigurationImportSelector 這個(gè)類的bean定義

我們都知道,如果這個(gè)類實(shí)現(xiàn)了ImportSelector接口,那他肯定重寫了一個(gè)方法,就是我們上面重寫過(guò)的selectImports方法:

果然,在這個(gè)類里面確實(shí)有這個(gè)selectImports方法:

我的天,好長(zhǎng)的一串代碼,一行都放不下!

此時(shí)此刻,我又回想起了在家鄉(xiāng)的母親,夏天的蟬鳴,池塘的荷花…

等等等等,這個(gè)類我們當(dāng)時(shí)返回的是什么?是一個(gè)字符串?dāng)?shù)組String[ ],那這個(gè)類無(wú)論多么長(zhǎng),返回的肯定就是一個(gè)字符串?dāng)?shù)組,不信你自己看:

這個(gè)字符串?dāng)?shù)組存放的內(nèi)容我們是否清楚呢?當(dāng)然清楚了!我們返回的是要加載的Config配置文件的全包名,通過(guò)返回這個(gè)全包名,我們就能自動(dòng)裝配上這些配置文件下定義的bean對(duì)象,從而達(dá)到了自動(dòng)裝配的目的!

根據(jù)剛才我們自己實(shí)現(xiàn)的selectImports方法,我們是通過(guò)注解類的名字來(lái)查找,并且最終得到需要加載的Config類的全類名,最后返回的。

因此,這里必然有一個(gè)根據(jù)注解類名字來(lái)查找相應(yīng)的Config文件的操作

我們繼續(xù)反推,看到返回時(shí)的定義如下:

我們發(fā)現(xiàn)autoConfigurationEntry中保存著我們需要的配置信息,它是通過(guò)getAutoConfigurationEntry方法獲取的,于是我們繼續(xù)深入,進(jìn)入getAutoConfigurationEntry方法

這一段代碼真是把人難住了,好大一片,不知道在做什么

此時(shí)此刻,我又回想起了在家鄉(xiāng)的母親,夏天的蟬鳴,池塘的荷花…

回家!有了!我們先想這個(gè)方法應(yīng)該返回什么,根據(jù)我們前面的經(jīng)驗(yàn),這里應(yīng)該返回一個(gè)類似于Entry的保存了我們需要的配置信息的對(duì)象

這個(gè)方法返回的是新建的AutoConfigurationEntry對(duì)象,根據(jù)最后一行的構(gòu)造函數(shù)來(lái)看,給他了兩個(gè)參數(shù)

configurations, exclusions

configurations顯然使我們需要的配置文件,也是我們最關(guān)心的,而exclusions字面意思是排除,也就是不需要的,那我們接下來(lái)應(yīng)該關(guān)注configurations到底是怎么來(lái)的

根據(jù)我們前面的經(jīng)驗(yàn),我們是根據(jù)注解類名來(lái)從一個(gè)配置文件中讀取出我們需要的Config配置類,這里configurations就代表了Config配置類,那么我們應(yīng)該找到一個(gè)入口,這個(gè)入口跟注解相關(guān),并且返回了configurations這個(gè)參數(shù)。

正如我們所料,這個(gè)方法的參數(shù)確實(shí)傳遞過(guò)來(lái)了一個(gè)東西,跟注解有關(guān):

看見(jiàn)那個(gè)大大的Annotation(注解)了嗎!

那么根據(jù)這條”線索“,我們按圖索驥,找到了三行代碼,范圍進(jìn)一步縮小了!

此時(shí)再加上返回了configurations,我們最終確定了一行代碼:

就是這個(gè)getCandidateConfigurations方法,符合我們的要求!

從字面意思上分析,獲取候選的配置,確實(shí)是我們需要的方法

OK,讓我們繼續(xù)前進(jìn),進(jìn)入這個(gè)方法:

這個(gè)方法是不是也似曾相識(shí)呢?我們之前寫過(guò)一個(gè)專門用于讀取配置文件的類MyPropertyReader,還記得嗎?

如果你還記得的話,我們自己寫的工具類里面也是一個(gè)靜態(tài)方法readPropertyForMe來(lái)幫我讀取配置文件

但是我們的配置文件路徑一定是需要指定的,不能亂放。

從這個(gè)loadFactoryNames方法體來(lái)看,好像沒(méi)有給他傳遞一個(gè)具體路徑

但是從下面的Assert斷言中,我們發(fā)現(xiàn)了玄機(jī):

在META-INF/spring.factories文件中沒(méi)有找到自動(dòng)配置類Config,你要檢查balabala。。。。

根據(jù)我不太靈光的腦袋的判斷,他的這個(gè)配置文件就叫spring.factories,存放的路徑是META-INF/spring.factories

于是我們打開spring boot自動(dòng)裝配的依賴jar包:

那這個(gè)配置文件里面的內(nèi)容,是不是跟我們想的一樣呢?

原來(lái)如此。

這里的EnableAutoConfiguration注解,正是我們此行的起點(diǎn)啊…

到這里,自動(dòng)裝配到底是什么,應(yīng)該比較清楚了,原來(lái)他是幫我們加載了各種已經(jīng)寫好的Config類文件,實(shí)現(xiàn)了這些JavaConfig配置文件的重復(fù)利用和組件化

7.4 loadFactoryNames方法

行程不能到此結(jié)束,學(xué)習(xí)不能淺嘗輒止。

我們還有最后一塊(幾塊)面紗沒(méi)有解開,現(xiàn)在還不能善罷甘休。

讓我們進(jìn)入loadFactoryNames方法:

這個(gè)方法非常簡(jiǎn)短,因?yàn)樗{(diào)用了真正實(shí)現(xiàn)的方法:loadSpringFactories

這一行return代碼我復(fù)制在下面:

loadSpringFactories(classLoader)
     .getOrDefault(factoryTypeName, Collections.emptyList());

可以分析得出:loadSpringFactories方法的返回值又調(diào)用了一個(gè)getOrDefault方法,這明顯是一個(gè)容器類的方法,目的是從容器中拿點(diǎn)東西出來(lái)

就此推測(cè):loadSpringFactories返回了一個(gè)包含我們需要的Config全類名(字符串)的集合容器,然后從這個(gè)集合容器中拿出來(lái)的東西就是我們的configurations

讓我們看這個(gè)loadSpringFactories方法:

它確實(shí)返回了一個(gè)容器:Map 這個(gè)容器的類型是:MultiValueMap

這個(gè)數(shù)據(jù)結(jié)構(gòu)就非常牛逼了,多值集合映射(我自己的翻譯)簡(jiǎn)單來(lái)說(shuō),一個(gè)key可以對(duì)應(yīng)多個(gè)value,根據(jù)他的返回值,我們可以看到在這個(gè)方法中一個(gè)String對(duì)應(yīng)了一個(gè)List

那么不難想到MultiValueMap中存放的形式:是”注解的類名——多個(gè)Config配置類“ 讓我們打個(gè)斷點(diǎn)來(lái)驗(yàn)證一下:

果然是這樣,并且@EnableAutoConfiguration注解竟然加載了多達(dá)124個(gè)配置類!

接下來(lái)我們繼續(xù)思考:我們來(lái)的目的是獲取configurations,所以無(wú)論你做什么,必須得讀取配置文件,拿到configurations

于是我們?cè)趖ry方法體中果然發(fā)現(xiàn)了這個(gè)操作:

他獲取了一個(gè)路徑urls,那么這個(gè)路徑是否就是我們前面驗(yàn)證的META-INF/spring.factories呢?

我們查看靜態(tài)常量FACTORIES_RESOURCE_LOCATION的值:

果真如此,bingo!繼續(xù)往下看,果然他遍歷了urls中的內(nèi)容,從這個(gè)路徑加載了配置文件:終于看到了我們熟悉的loadProperties方法!

那我們大概就知道了,他確實(shí)是通過(guò)找到路徑,然后根據(jù)路徑讀取了配置文件,然后返回了讀取的result

這就是loadFactoryNames方法的內(nèi)部實(shí)現(xiàn)。

7.5 cache探秘

到這里有的人又要問(wèn)了:是不是結(jié)束了?其實(shí)還遠(yuǎn)沒(méi)有!

細(xì)心地朋友已經(jīng)發(fā)現(xiàn)了玄機(jī),隱藏在loadFactoryNames方法的開頭和結(jié)尾:

喂喂,這個(gè)返回的result好像并不是直接new出來(lái)的哦

它是從cache緩存中取出來(lái)的,你發(fā)現(xiàn)了沒(méi)有

根據(jù)下面的if判斷,如果從緩存中讀取出來(lái)了result,并且result的結(jié)果不為空,就直接返回,不需要再進(jìn)行下面的讀寫操作了,這樣減少了磁盤頻繁的讀寫I/O

同理,在我更新完所有的配置文件資源之后,退出時(shí)也要更新緩存。

7.6 getAutoConfigurationEntry再探

關(guān)鍵部分已經(jīng)過(guò)去,讓我們反過(guò)頭來(lái)重新審視一下遺漏的內(nèi)容:

還記得getAutoConfigurationEntry方法嗎?

我們最后來(lái)研究一下這個(gè)類除了getCandidateConfigurations還干了哪些事情:

  • removeDuplicates
  • configurations.removeAll(exclusions)

可以看到,這里對(duì)加載進(jìn)來(lái)的配置進(jìn)行了去重、排除的操作,這是為了使得用戶自定義的排除包生效,同時(shí)避免包沖突異常,在SpringBoot的入口函數(shù)中我們可以通過(guò)注解指定需要排除哪些不用的包:

例如我不使用RabbitMQ的配置包,就把它的配置類的class傳給exclude

@SpringBootApplication(exclude = {RabbitAutoConfiguration.class})

8. 自動(dòng)裝配本質(zhì)

我的理解:

  • SpringBoot自動(dòng)裝配的本質(zhì)就是通過(guò)Spring去讀取META-INF/spring.factories中保存的配置類文件然后加載bean定義的過(guò)程。
  • 如果是標(biāo)了@Configuration注解,就是批量加載了里面的bean定義
  • 如何實(shí)現(xiàn)”自動(dòng)“:通過(guò)配置文件獲取對(duì)應(yīng)的批量配置類,然后通過(guò)配置類批量加載bean定義,只要有寫好的配置文件spring.factories就實(shí)現(xiàn)了自動(dòng)。

9. 總結(jié)

Spring Boot的自動(dòng)裝配特性可以說(shuō)是Spring Boot最重要、最核心的一環(huán),正是因?yàn)檫@個(gè)特性,使得我們的生產(chǎn)復(fù)雜性大大降低,極大地簡(jiǎn)化了開發(fā)流程,可以說(shuō)是給我們帶來(lái)了巨大的福音了~~

筆者本人對(duì)源碼的理解仍然沒(méi)有那么深刻,只是喜歡分享自己的一些學(xué)習(xí)經(jīng)驗(yàn),希望能和大家共同學(xué)習(xí),畢竟掌握一門新技術(shù)的快感嘛… 大家都懂的!

寫這篇文章耗費(fèi)了巨大的精力,每一個(gè)字均是手碼,真的希望喜歡的朋友可以點(diǎn)贊收藏關(guān)注支持一波,這就是對(duì)我這個(gè)未出世的學(xué)生的最大激勵(lì)了!

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    335

    瀏覽量

    14278
  • 源碼分析
    +關(guān)注

    關(guān)注

    0

    文章

    5

    瀏覽量

    5537
  • 自動(dòng)裝配
    +關(guān)注

    關(guān)注

    0

    文章

    7

    瀏覽量

    641
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    protel技術(shù)大全--初學(xué)者必看

    protel技術(shù)大全--初學(xué)者必看。
    發(fā)表于 08-04 10:36

    PSOC1初學(xué)者必看的10個(gè)實(shí)例

    PSOC1初學(xué)者必看的10個(gè)實(shí)例
    發(fā)表于 11-19 16:33

    MATLAB入門教程-初學(xué)者必看

    MATLAB入門教程-初學(xué)者必看
    發(fā)表于 06-28 15:39

    Linux初學(xué)者必看!?。?/a>

    Linux初學(xué)者必看?。?!
    發(fā)表于 01-07 21:35

    單片機(jī)入門秘籍,初學(xué)者必看

    初學(xué)者必看的單片機(jī)秘籍
    發(fā)表于 04-29 16:10

    PCB LAYOUT初學(xué)者必看!

    PCBLAYOUT技術(shù)大全---初學(xué)者必看! PROTEL相關(guān)疑問(wèn) 1.原理圖常見(jiàn)錯(cuò)誤: (1)ERC報(bào)告管腳沒(méi)有接入信號(hào): a.創(chuàng)建封裝時(shí)給管腳定義了I/O屬性; b.創(chuàng)建元件或放置元件時(shí)修改了不一致的grid屬性
    發(fā)表于 09-13 15:23 ?0次下載

    PSOC1初學(xué)者5個(gè)實(shí)驗(yàn),針對(duì)初學(xué)者的實(shí)驗(yàn)

    PSOC1初學(xué)者5個(gè)實(shí)驗(yàn),針對(duì)初學(xué)者的實(shí)驗(yàn)
    發(fā)表于 10-16 09:33 ?14次下載
    PSOC1<b class='flag-5'>初學(xué)者</b>5個(gè)實(shí)驗(yàn),針對(duì)<b class='flag-5'>初學(xué)者</b>的實(shí)驗(yàn)

    初學(xué)者必看的基本電子技術(shù)概念

    初學(xué)者必看的基本電子技術(shù)概念
    發(fā)表于 05-17 11:41 ?0次下載

    初學(xué)者必看的電源測(cè)試項(xiàng)目要點(diǎn)及教程

    初學(xué)者必看的電源測(cè)試項(xiàng)目要點(diǎn)及教程
    發(fā)表于 07-01 14:09 ?29次下載

    初學(xué)者必看的LABVIEW工程師編程經(jīng)驗(yàn)

    初學(xué)者必看的LABVIEW工程師編程經(jīng)驗(yàn)
    發(fā)表于 07-12 14:24 ?29次下載

    ARM與嵌入式linux入門的建議(初學(xué)者必看)

    ARM與嵌入式linux入門的建議(初學(xué)者必看)(嵌入式開發(fā)培訓(xùn)怎么樣)-該文檔為ARM與嵌入式linux入門的建議(初學(xué)者必看)總結(jié)文檔,是一份很不錯(cuò)的參考資料,具有較高參考價(jià)值,感
    發(fā)表于 08-04 10:02 ?15次下載
    ARM與嵌入式linux入門的建議(<b class='flag-5'>初學(xué)者</b><b class='flag-5'>必看</b>)

    初學(xué)者必看的單片機(jī)程序匯總

    初學(xué)者必看的單片機(jī)程序匯總
    發(fā)表于 09-15 14:33 ?49次下載

    初學(xué)者必看SpringBoo自動(dòng)裝配原理1

    學(xué)習(xí)SpringBoot,絕對(duì)避不開自動(dòng)裝配這個(gè)概念,這也是SpringBoot的關(guān)鍵之一 本人也是SpringBoot的初學(xué)者,下面的一些總結(jié)都是結(jié)合個(gè)人理解和實(shí)踐得出的,如果有錯(cuò)誤或者疏漏,請(qǐng)一定一定一定(不是歡迎,是
    的頭像 發(fā)表于 04-07 11:03 ?583次閱讀
    <b class='flag-5'>初學(xué)者</b><b class='flag-5'>必看</b>的<b class='flag-5'>SpringBoo</b><b class='flag-5'>自動(dòng)</b><b class='flag-5'>裝配</b>原理1

    初學(xué)者必看SpringBoo自動(dòng)裝配原理2

    學(xué)習(xí)SpringBoot,絕對(duì)避不開自動(dòng)裝配這個(gè)概念,這也是SpringBoot的關(guān)鍵之一 本人也是SpringBoot的初學(xué)者,下面的一些總結(jié)都是結(jié)合個(gè)人理解和實(shí)踐得出的,如果有錯(cuò)誤或者疏漏,請(qǐng)一定一定一定(不是歡迎,是
    的頭像 發(fā)表于 04-07 11:03 ?518次閱讀

    初學(xué)者必看SpringBoo自動(dòng)裝配原理3

    學(xué)習(xí)SpringBoot,絕對(duì)避不開自動(dòng)裝配這個(gè)概念,這也是SpringBoot的關(guān)鍵之一 本人也是SpringBoot的初學(xué)者,下面的一些總結(jié)都是結(jié)合個(gè)人理解和實(shí)踐得出的,如果有錯(cuò)誤或者疏漏,請(qǐng)一定一定一定(不是歡迎,是
    的頭像 發(fā)表于 04-07 11:03 ?513次閱讀
    <b class='flag-5'>初學(xué)者</b><b class='flag-5'>必看</b>的<b class='flag-5'>SpringBoo</b><b class='flag-5'>自動(dòng)</b><b class='flag-5'>裝配</b>原理3