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

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

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

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

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

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

來自丨CSDN

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

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

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

Spring容器在實例化時會加載容器內(nè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); //實例化Bean

}

}

}

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

在BeanFactory實例化所有非延遲加載的單例Bean時,遍歷beanDefinitionNames 集合,按順序?qū)嵗付Q的Bean。beanDefinitionNames 屬性是Spring在加載Bean Class生成的BeanDefinition時,為這些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在加載一個BeanDefinition(也就是加載Bean Class)時,將相應(yīng)的beanName存入beanDefinitionNames屬性中,在加載完所有的BeanDefinition后,執(zhí)行Bean實例化工作,此時會依據(jù)beanDefinitionNames的順序來有序?qū)嵗疊ean,也就是說Spring容器內(nèi)Bean的加載和實例化是有順序的,而且近似一致,當(dāng)然僅是近似。

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

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

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

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

如果未解析過,那么開始解析:

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

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

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

解析@Import引入的類,如果這個類是一個配置類,則遞歸解析。

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

解析父類上的@ComponentScan,@Import,@Bean,父類不會被再次實例化,因為其子類能夠做父類的工作,不需要額外的Bean了。

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

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

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

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

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

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

此時啟動Spring容器,將A引入容器內(nèi)。

如果A是通過@ComponentScan掃描到的,那么此時的加載順序是:

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

如果A是通過@Import形式引入的,那么此時的加載順訊是:

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

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

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

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

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

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

編輯:jq

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

    關(guān)注

    0

    文章

    335

    瀏覽量

    14278

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

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

收藏 人收藏

    評論

    相關(guān)推薦

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

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

    labview CAN DBC加載解析程序

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

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

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

    運動控制器的代碼運行順序是什么

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

    PLC順序啟動逆順序停止電路如何運行

    了解了順序啟動電路,今天再來看看順序啟動逆順序停止電路如何運行。
    發(fā)表于 04-09 14:13 ?618次閱讀
    PLC<b class='flag-5'>順序</b>啟動逆<b class='flag-5'>順序</b>停止電路如何運行

    PLC順序功能圖(SFC)編程深入解析

    稱為順序功能圖 (SFC) 的圖形編程語言由 IEC 61131-3 標(biāo)準(zhǔn)。該圖表從上到下閱讀,塊用于表示步驟、轉(zhuǎn)換和功能。這種編程風(fēng)格類似于功能框圖,并且具有一些相同的缺陷,這些缺陷會產(chǎn)生復(fù)雜性并降低編程效率。
    發(fā)表于 03-21 11:45 ?8552次閱讀
    PLC<b class='flag-5'>順序</b>功能圖(SFC)編程深入<b class='flag-5'>解析</b>

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

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

    LED樹木燈光亮方案的設(shè)計與控制技術(shù)解析

    LED樹木燈光亮方案的設(shè)計與控制技術(shù)解析
    的頭像 發(fā)表于 01-24 17:54 ?590次閱讀
    LED樹木燈光亮<b class='flag-5'>化</b>方案的設(shè)計與控制技術(shù)<b class='flag-5'>解析</b>

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

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

    idea 配置 tomcat找不到或無法加載主類

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

    java web 三大組件 啟動順序

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

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

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

    c語言中順序結(jié)構(gòu)的基本流程

    C語言是一種通用的編程語言,注重結(jié)構(gòu)編程和順序結(jié)構(gòu)。順序結(jié)構(gòu)是C語言編程中最簡單也是最基本的控制結(jié)構(gòu)之一,它按照代碼的編寫順序一行一行地執(zhí)行代碼,沒有任何分支或循環(huán)。
    的頭像 發(fā)表于 11-22 10:20 ?869次閱讀

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

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

    車規(guī)MCU的啟動加載程序是什么

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