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

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

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

解析加載及實(shí)例化Bean的順序(零配置)

5jek_harmonyos ? 來(lái)源:CSDN ? 作者:低調(diào)的JVM ? 2021-08-04 16:08 ? 次閱讀

作者丨低調(diào)的JVM

來(lái)自丨CSDN

https://blog.csdn.net/qq_27529917/article/details/79329809

在使用Spring時(shí),Bean之間會(huì)有些依賴,比如一個(gè)Bean A實(shí)例化時(shí)需要用到Bean B,那么B應(yīng)該在A之前實(shí)例化好。很多時(shí)候Spring智能地為我們做好了這些工作,但某些情況下可能不是,比如Springboot的@AutoConfigureAfter注解,手動(dòng)的指定Bean的實(shí)例化順序。

了解Spring內(nèi)Bean的解析,加載和實(shí)例化順序機(jī)制有助于我們更好的使用Spring/Springboot,避免手動(dòng)的去干預(yù)Bean的加載過(guò)程,搭建更優(yōu)雅的框架。

Spring容器在實(shí)例化時(shí)會(huì)加載容器內(nèi)所有非延遲加載的單例類(lèi)型Bean,看如下源碼:

public abstract class AbstractApplicationContext extends DefaultResourceLoader

implements ConfigurableApplicationContext, DisposableBean {

//刷新Spring容器,相當(dāng)于初始化

public void refresh() throws BeansException, IllegalStateException {

。。。。。。

// Instantiate all remaining (non-lazy-init) singletons.

finishBeanFactoryInitialization(beanFactory);

}

}

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory

implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

/** List of bean definition names, in registration order */

private volatile List《String》 beanDefinitionNames = new ArrayList《String》(256);

public void preInstantiateSingletons() throws BeansException {

List《String》 beanNames = new ArrayList《String》(this.beanDefinitionNames);

for (String beanName : beanNames) {

。。。。。。

getBean(beanName); //實(shí)例化Bean

}

}

}

ApplicationContext內(nèi)置一個(gè)BeanFactory對(duì)象,作為實(shí)際的Bean工廠,和Bean相關(guān)業(yè)務(wù)都交給BeanFactory去處理。

在BeanFactory實(shí)例化所有非延遲加載的單例Bean時(shí),遍歷beanDefinitionNames 集合,按順序?qū)嵗付Q的Bean。beanDefinitionNames 屬性是Spring在加載Bean Class生成的BeanDefinition時(shí),為這些Bean預(yù)先定義好的名稱,看如下代碼:

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory

implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)

throws BeanDefinitionStoreException {

。。。。。。

this.beanDefinitionNames.add(beanName);

}

}

BeanFactory在加載一個(gè)BeanDefinition(也就是加載Bean Class)時(shí),將相應(yīng)的beanName存入beanDefinitionNames屬性中,在加載完所有的BeanDefinition后,執(zhí)行Bean實(shí)例化工作,此時(shí)會(huì)依據(jù)beanDefinitionNames的順序來(lái)有序?qū)嵗疊ean,也就是說(shuō)Spring容器內(nèi)Bean的加載和實(shí)例化是有順序的,而且近似一致,當(dāng)然僅是近似。

Spring在初始化容器時(shí),會(huì)先解析和加載所有的Bean Class,如果符合要求則通過(guò)Class生成BeanDefinition,存入BeanFactory中,在加載完所有Bean Class后,開(kāi)始有序的通過(guò)BeanDefinition實(shí)例化Bean。

我們先看加載Bean Class過(guò)程,零配置下Spring Bean的加載起始于ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)方法,我總結(jié)了下其加載解析Bean Class的流程:

配置類(lèi)可以是Spring容器的起始配置類(lèi),也可以是通過(guò)@ComponentScan掃描得到的類(lèi),也可以是通過(guò)@Import引入的類(lèi)。如果這個(gè)類(lèi)上含有@Configuration,@Component,@ComponentScan,@Import,@ImportResource注解中的一個(gè),或者內(nèi)部含有@Bean標(biāo)識(shí)的方法,那么這個(gè)類(lèi)就是一個(gè)配置類(lèi),Spring就會(huì)按照一定流程去解析這個(gè)類(lèi)上的信息。

在解析的第一步會(huì)校驗(yàn)當(dāng)前類(lèi)是否已經(jīng)被解析過(guò)了,如果是,那么需要按照一定的規(guī)則處理(@ComponentScan得到的Bean能覆蓋@Import得到的Bean,@Bean定義的優(yōu)先級(jí)最高)。

如果未解析過(guò),那么開(kāi)始解析:

解析內(nèi)部類(lèi),查看內(nèi)部類(lèi)是否應(yīng)該被定義成一個(gè)Bean,如果是,遞歸解析。

解析@PropertySource,也就是解析被引入的Properties文件。

解析配置類(lèi)上是否有@ComponentScan注解,如果有則執(zhí)行掃描動(dòng)作,通過(guò)掃描得到的Bean Class會(huì)被立即解析成BeanDefinition,添加進(jìn)beanDefinitionNames屬性中。之后查看掃描到的Bean Class是否是一個(gè)配置類(lèi)(大部分情況是,因?yàn)闃?biāo)識(shí)@Component注解),如果是則遞歸解析這個(gè)Bean Class。

解析@Import引入的類(lèi),如果這個(gè)類(lèi)是一個(gè)配置類(lèi),則遞歸解析。

解析@Bean標(biāo)識(shí)的方法,此種形式定義的Bean Class不會(huì)被遞歸解析

解析父類(lèi)上的@ComponentScan,@Import,@Bean,父類(lèi)不會(huì)被再次實(shí)例化,因?yàn)槠渥宇?lèi)能夠做父類(lèi)的工作,不需要額外的Bean了。

在1,3,4,6中都有遞歸操作,也就是在解析一個(gè)Bean Class A時(shí),發(fā)現(xiàn)其上能夠獲取到其他Bean Class B信息,此時(shí)會(huì)遞歸的解析Bean Class B,在解析完Bean Class B后再接著解析Bean Class A,可能在解析B時(shí)能夠獲取到C,那么也會(huì)先解析C再解析B,就這樣不斷的遞歸解析。

在第3步中,通過(guò)@ComponentScan掃描直接得到的Bean Class會(huì)被立即加載入beanDefinitionNames中,但@Import和@Bean形式定義的Bean Class則不會(huì),也就是說(shuō)正常情況下面@ComponentScan直接得到的Bean其實(shí)例化時(shí)機(jī)比其他兩種形式的要早。

通過(guò)@Bean和@Import形式定義的Bean Class不會(huì)立即加載,他們會(huì)被放入一個(gè)ConfigurationClass類(lèi)中,然后按照解析的順序有序排列,就是圖片上的 “將配置類(lèi)有序排列”。一個(gè)ConfigurationClass代表一個(gè)配置類(lèi),這個(gè)類(lèi)可能是被@ComponentScan掃描到的,則此類(lèi)已經(jīng)被加載過(guò)了;也可能是被@Import引入的,則此類(lèi)還未被加載;此類(lèi)中可能含有@Bean標(biāo)識(shí)的方法。

Spring在解析完了所有Bean Class后,開(kāi)始加載ConfigurationClass。如果這個(gè)ConfigurationClass是被Import的,也就是說(shuō)在加載@ComponentScan時(shí)其未被加載,那么此時(shí)加載ConfigurationClass代表的Bean Class。然后加載ConfigurationClass內(nèi)的@Bean方法。

順序總結(jié):@ComponentScan 》 @Import 》 @Bean

Bean Class的結(jié)構(gòu)圖如上所示,A是配置類(lèi)的入口,通過(guò)A能直接或間接的引入一個(gè)模塊。

此時(shí)啟動(dòng)Spring容器,將A引入容器內(nèi)。

如果A是通過(guò)@ComponentScan掃描到的,那么此時(shí)的加載順序是:

A 》 D 》 F 》 B 》 E 》 G 》 C

如果A是通過(guò)@Import形式引入的,那么此時(shí)的加載順訊是:

D 》 F 》 B 》 E 》 G 》 A 》 C

當(dāng)然以上僅僅代表著加載Bean Class的順序,實(shí)際實(shí)例化Bean的順序和加載順序大體相同,但還是會(huì)有一些差別。

Spring在通過(guò)getBean(beanName)形式實(shí)例化Bean時(shí),會(huì)通過(guò)BeanDefinition去生成Bean對(duì)象。在這個(gè)過(guò)程中,如果BeanDefinition的DependsOn不為空,從字面理解就是依賴某個(gè)什么,其值一般是某個(gè)或多個(gè)beanName,也就是說(shuō)依賴于其他Bean。

此時(shí)Spring會(huì)將DependsOn指定的這些名稱的Bean先實(shí)例化,也就是先調(diào)用getBean(dependsOn)方法。我們可以通過(guò)在Bean Class或者@Bean的方法上標(biāo)識(shí)**@DependsOn**注解,來(lái)指定當(dāng)前Bean實(shí)例化時(shí)需要觸發(fā)哪些Bean的提前實(shí)例化。

當(dāng)一個(gè)Bean A內(nèi)部通過(guò)@Autowired或者@Resource注入Bean B,那么在實(shí)例化A時(shí)會(huì)觸發(fā)B的提前實(shí)例化,此時(shí)會(huì)注冊(cè)A》B的dependsOn依賴關(guān)系,實(shí)質(zhì)和@DependsOn一樣,這個(gè)是Spring自動(dòng)為我們處理好的。

了解Spring Bean的解析,加載及實(shí)例化的順序機(jī)制能夠加深對(duì)Spring的理解,搭建更優(yōu)雅簡(jiǎn)介的Spring框架。

編輯:jq

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(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

    瀏覽量

    14259

原文標(biāo)題:Spring解析,加載及實(shí)例化Bean的順序(零配置)

文章出處:【微信號(hào):harmonyos_developer,微信公眾號(hào):harmonyos_developer】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    labview實(shí)現(xiàn)DBC在界面加載配置

    labview實(shí)現(xiàn)DBC在界面加載配置
    發(fā)表于 08-19 14:27 ?25次下載

    labview CAN DBC加載解析程序

    labview CAN DBC加載解析程序
    發(fā)表于 08-18 11:42

    【電磁兼容標(biāo)準(zhǔn)解析分享】汽車(chē)電子部件EMC標(biāo)準(zhǔn)解析---你應(yīng)該了解和知道的細(xì)節(jié)(二)

    【電磁兼容標(biāo)準(zhǔn)解析分享】汽車(chē)電子部件EMC標(biāo)準(zhǔn)解析---你應(yīng)該了解和知道的細(xì)節(jié)(二)
    的頭像 發(fā)表于 08-08 08:17 ?3111次閱讀
    【電磁兼容標(biāo)準(zhǔn)<b class='flag-5'>解析</b>分享】汽車(chē)電子<b class='flag-5'>零</b>部件EMC標(biāo)準(zhǔn)<b class='flag-5'>解析</b>---你應(yīng)該了解和知道的細(xì)節(jié)(二)

    運(yùn)動(dòng)控制器的代碼運(yùn)行順序是什么

    組成部分和關(guān)鍵步驟。 初始 運(yùn)動(dòng)控制器的代碼運(yùn)行順序首先從初始化開(kāi)始。初始是為控制器設(shè)置初始狀態(tài)的過(guò)程,包括配置輸入/輸出接口、設(shè)置寄存器、初始
    的頭像 發(fā)表于 06-13 09:25 ?301次閱讀

    MCU單片機(jī)GPIO初始該按什么順序配置?為什么初始化時(shí)有電平跳變?

    GPIO初始化時(shí)有時(shí)鐘配置、模式配置、輸出配置、復(fù)用配置,那么在編寫(xiě)初始代碼時(shí),到底該按什么順序
    的頭像 發(fā)表于 02-22 11:07 ?1168次閱讀
    MCU單片機(jī)GPIO初始<b class='flag-5'>化</b>該按什么<b class='flag-5'>順序</b><b class='flag-5'>配置</b>?為什么初始化時(shí)有電平跳變?

    AD9208的寄存器配置順序是怎樣的?

    是使用verilog語(yǔ)言寫(xiě)的寄存器配置,由FPGA燒寫(xiě)進(jìn)去,但無(wú)論我如何配置ADC9208的寄存器值和寄存器配置順序,使用Vivado抓取的ADC輸出值都是0,請(qǐng)問(wèn)我這種AD9208的
    發(fā)表于 12-05 07:20

    idea 配置 tomcat找不到或無(wú)法加載主類(lèi)

    配置和部署Tomcat過(guò)程中,有時(shí)可能會(huì)遇到找不到或無(wú)法加載主類(lèi)的問(wèn)題。這個(gè)問(wèn)題可能是由于多種原因引起的,包括錯(cuò)誤的配置、JDK版本不匹配、依賴庫(kù)缺失等。本文將詳細(xì)介紹在配置Tomc
    的頭像 發(fā)表于 12-03 15:26 ?1856次閱讀

    java web 三大組件 啟動(dòng)順序

    ,容器會(huì)首先加載并初始所有的Servlet。Servlet的初始過(guò)程包括執(zhí)行init方法,該方法用于進(jìn)行一些必要的初始化工作,比如數(shù)
    的頭像 發(fā)表于 12-03 11:45 ?746次閱讀

    sql where條件的執(zhí)行順序

    。 在深入討論WHERE條件的執(zhí)行順序之前,先回顧一下一般SQL語(yǔ)句的執(zhí)行順序。一條SQL語(yǔ)句的執(zhí)行通常可以分為以下幾個(gè)步驟:解析器分析語(yǔ)法、語(yǔ)義校驗(yàn)、查詢優(yōu)化器生成執(zhí)行計(jì)劃、執(zhí)行計(jì)劃生成與執(zhí)行。在這個(gè)過(guò)程中,WHERE條件的執(zhí)
    的頭像 發(fā)表于 11-23 11:31 ?1862次閱讀

    圖解Spring Bean生成流程,非常詳盡

    以上問(wèn)題,都離不開(kāi)對(duì)bean生成流程的熟悉與理解。但是不得不談,目前網(wǎng)上文章魚(yú)龍混雜,一些偏頗錯(cuò)誤的分析四處流傳,我們后面會(huì)提到一些常見(jiàn)謬傳。至于現(xiàn)在,現(xiàn)在先和我們一起,深入的看下springBean的生成邏輯吧
    的頭像 發(fā)表于 10-31 15:36 ?690次閱讀
    圖解Spring <b class='flag-5'>Bean</b>生成流程,非常詳盡

    車(chē)規(guī)MCU的啟動(dòng)加載程序是什么

    加載程序負(fù)責(zé)在MCU上啟動(dòng)和加載應(yīng)用程序。它通過(guò)讀取存儲(chǔ)器中的引導(dǎo)加載代碼,并將其加載到MCU的內(nèi)部存儲(chǔ)器中執(zhí)行,從而啟動(dòng)應(yīng)用程序。 存儲(chǔ)器管理:?jiǎn)?dòng)
    的頭像 發(fā)表于 10-27 17:26 ?1376次閱讀

    BeanFactory 和 FactoryBean的區(qū)別

    的主要功能包括: Bean實(shí)例和管理 :BeanFactory負(fù)責(zé)創(chuàng)建、初始和管理Bean的生命周期。它會(huì)根據(jù)
    的頭像 發(fā)表于 10-16 10:12 ?903次閱讀

    類(lèi)隔離實(shí)現(xiàn)之自定義類(lèi)加載器的擴(kuò)展

    機(jī)制。 2、類(lèi)加載是什么? 類(lèi)加載是一種過(guò)程,是將class文件加載到j(luò)vm內(nèi)存的過(guò)程。當(dāng)代碼邏輯中需要引用類(lèi)時(shí),通過(guò)類(lèi)加載加載引用類(lèi)對(duì)象
    的頭像 發(fā)表于 10-08 15:17 ?507次閱讀

    驅(qū)動(dòng)動(dòng)模塊之間如何更改模塊掛載順序

    fs_initcall 去聲明。 2、對(duì)于同一優(yōu)先級(jí)的驅(qū)動(dòng)模塊,可以在 Makefile 中更改其編譯和鏈接的順序,就會(huì)切換其掛載的順序。(靜態(tài)編譯) 3、動(dòng)態(tài)加載驅(qū)動(dòng)模塊:等 Linux 系統(tǒng)起來(lái)以后,手動(dòng)執(zhí)行 insmod
    的頭像 發(fā)表于 09-28 16:54 ?1279次閱讀
    驅(qū)動(dòng)動(dòng)模塊之間如何更改模塊掛載<b class='flag-5'>順序</b>

    華為防火墻的安全策略配置實(shí)例

    今天給大家介紹華為防火墻的安全策略配置實(shí)例。本文采用華為eNSP模擬器,設(shè)計(jì)了一個(gè)USG6000系列防火墻的配置實(shí)例,并安全要求完成了相應(yīng)配置
    的頭像 發(fā)表于 09-22 09:36 ?6297次閱讀
    華為防火墻的安全策略<b class='flag-5'>配置</b><b class='flag-5'>實(shí)例</b>