最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring中Bean的創(chuàng)建流程詳細(xì)解讀

 更新時(shí)間:2023年10月30日 08:47:33   作者:荊軻刺秦  
這篇文章主要介紹了Spring中Bean的創(chuàng)建流程詳細(xì)解讀,Spring 中創(chuàng)建 Bean ,是通過調(diào)用 GetBean 方法來(lái)觸發(fā)的,所以,我們會(huì)從這個(gè)方法開始,需要的朋友可以參考下

前言

文章詳細(xì)描述一個(gè) Bean 誕生的過程,而文章的目錄結(jié)構(gòu),也清晰地反映了整個(gè)流程。

Spring 中創(chuàng)建 Bean ,是通過調(diào)用 GetBean 方法來(lái)觸發(fā)的。所以,我們會(huì)從這個(gè)方法開始。這篇文章不會(huì)粘貼源碼,我會(huì)嘗試以簡(jiǎn)明流暢的語(yǔ)言來(lái)梳理整個(gè)過程。在最后我可能沒能達(dá)到這個(gè)目標(biāo),但讓你明白我并不是從一開始就有意將文章復(fù)雜化也是有意義的。

1. 轉(zhuǎn)換 bean 名稱

看見這一步,或許會(huì)有所疑惑,但這確實(shí)是第一步,因?yàn)槲覀冃枰话?ldquo;鑰匙”。這一步是將我們獲取 Bean 時(shí)所指定的名稱,轉(zhuǎn)換為 Spring 容器中所管理 Bean 的真實(shí)名稱。

例如,在我們通過別名獲取 Bean 時(shí), 這一步則會(huì)將別名轉(zhuǎn)換為容器中 Bean 真實(shí)的名稱。

或者,在我們實(shí)現(xiàn) FactoryBean 接口以期待通過工廠獲取 Bean 時(shí)。在想要獲取真正的工廠類而不是所管理的 Bean ,這一步則會(huì)去掉前綴 &,因?yàn)樵谌萜髦写鎯?chǔ)的工廠 Bean 的真實(shí)名稱是沒有前綴的。但如果不去掉前綴,則獲取到的是工廠所管理的 Bean,這在后文有案例。

如果單純地將容器看作一個(gè)鍵值對(duì)的話,這一步就是去獲取鍵的。

2. 從三級(jí)緩存中獲取

在獲取到真實(shí)的 beanName 后,會(huì)迫切地根據(jù)它去單例緩存中獲取已經(jīng)注冊(cè)過的單例。

這一步指的便是從三級(jí)緩存中獲取單例。在循環(huán)依賴的過程中,這一步能夠獲取到提前暴露的 Bean 實(shí)例。

3. 從父容器中獲取

如果在單例緩存中沒有獲取到,且該容器中不存在 beanName 對(duì)應(yīng)的 BeanDefinition 的話。那么,在父容器存在的情況下,將嘗試從父容器中獲取。

常見的 SpringMvc ,便會(huì)包含父子容器,將不同層次的 Bean 置于不同層次的容器中,也利于管理。

4. 合并 BeanDefinition

如果上述過程都未能成功獲取到 Bean 的話,就要考慮去創(chuàng)建 Bean 了。

首先需要準(zhǔn)備好 BeanDefinition 。如果一個(gè) BeanDefinition 指定了另一個(gè) BeanDefinition 作為 parent 的話,那么需要合并 BeanDefinition。

對(duì)應(yīng)的應(yīng)用場(chǎng)景是為一個(gè) Bean 指定了 parent 。我們可以把這看作“繼承”關(guān)系,但這并不是真正意義上的繼承關(guān)系。我想你能明白我這里描述的是什么。

5. 檢查依賴

準(zhǔn)備好 BeanDefinition 后,需要做依賴檢查。這指的是,如果我們對(duì)一個(gè) Bean 使用 @DependsOn 注解來(lái)顯式表明依賴關(guān)系的話,那這一步就能夠確保 DependsOn 注解中所指定的 Bean 會(huì)先創(chuàng)建。

6. 創(chuàng)建

現(xiàn)在,一切就緒,可以開始創(chuàng)建 Bean 了。

在創(chuàng)建 Bean 之前,補(bǔ)充一個(gè)域的概念,這也是 Bean 的特點(diǎn)之一。不同域的 Bean 創(chuàng)建邏輯是否相同呢?

其實(shí),真正創(chuàng)建 Bean 的代碼是相同的,只有一份。但基于不同的域在創(chuàng)建 Bean 的前后會(huì)有不同的前置和后置邏輯。下面我們將一一分析各域的不同,并將相同的 Bean 的創(chuàng)建過程放在最后一節(jié)。

6.1 單例 Bean

單例 Bean 創(chuàng)建前,仍然需要判斷緩存。盡管在一開始我們有判斷單例的緩存,但你想象這一場(chǎng)景:在創(chuàng)建 A 的過程中,上一步的依賴檢查發(fā)現(xiàn) A 顯示聲明依賴了 B,那么就會(huì)觸發(fā) B 的創(chuàng)建。但 B 又需要自動(dòng)裝配 A。這時(shí)又會(huì)觸發(fā) A 的創(chuàng)建,并創(chuàng)建成功,在最終回到 A 的創(chuàng)建過程時(shí),依賴檢查已經(jīng)結(jié)束,開始進(jìn)入單例 Bean A 的創(chuàng)建。但此時(shí) A 其實(shí)已經(jīng)被提前創(chuàng)建成功了。

所以,此處仍然需要檢查緩存。不過,這里檢測(cè)的只是一級(jí)緩存。若一級(jí)緩存singletonObjects 中存在該 Bean ,則直接返回。

6.2 原型 Bean

因?yàn)樵兔看味紩?huì)創(chuàng)建對(duì)象,所以不會(huì)存在從緩存中去獲取。但這里仍然需要做循環(huán)依賴的檢查,并且原型 Bean 間的循環(huán)依賴無(wú)論如何,都無(wú)法解決的。我們可以在腦海中仔細(xì)想一想,是不是這個(gè)道理。

6.3 其它域 Bean

常見的其它域有 request、session 和 application 域。這里的 application 在 web 應(yīng)用中指的便是 servletContext 級(jí)別的,也就是在一個(gè) web 應(yīng)用中有效。request 域僅在一次請(qǐng)求中有效,session 域僅在一次會(huì)話中有效。失效后,下次將重新創(chuàng)建。也可以將它們理解為單例,不過它們的單例限制范圍更小。

這些域需要尊重它們的范圍限制,例如,request 域無(wú)法在一個(gè) web 請(qǐng)求外部使用。并且,這些域創(chuàng)建的 Bean 會(huì)緩存在它們的范圍中,具體的可參考 Scope 接口對(duì)應(yīng)的各實(shí)現(xiàn)類,這也是其它域的不同點(diǎn)所在。

這里可能會(huì)有一點(diǎn)疑惑,我們經(jīng)常在 controller 中自動(dòng)裝配 ServletRequest,而 controller 作為單例對(duì)象,將在啟動(dòng)時(shí)完成創(chuàng)建并初始化。那么,這時(shí)注入的 request 是從哪里來(lái)的呢?

其實(shí),這是因?yàn)?WebApplicationContext 提前注冊(cè)了可以解析的依賴,然后將 ServletRequest 接口類型映射到 RequestObjectFactory 對(duì)象。這樣在填充 controller 的屬性時(shí),可以發(fā)現(xiàn) ServletRequest 的裝配值是 RequestObjectFactory。你可以把這里理解為一個(gè)作弊過程,如果正常途徑無(wú)法幫助我們,那么我們只能尋求 boss 幫助了,類似于這里的 WebApplicationContext。

RequestObjectFactory 也不是ServletRequest 類型,而是一個(gè) ObjectFactory 的實(shí)現(xiàn)類。所以最終會(huì)生成一個(gè) ServletRequest 代理,執(zhí)行請(qǐng)求則會(huì)委托給 RequestObjectFactory對(duì)象中返回的 ServletRequest。這個(gè) ServletRequest 就和我們真正的 Web 請(qǐng)求有關(guān)了。這種做法很巧妙的將啟動(dòng)時(shí)便需要的對(duì)象和運(yùn)行時(shí)才產(chǎn)生的對(duì)象通過代理關(guān)聯(lián)了起來(lái)。

6.4 真正的創(chuàng)建 Bean

前文說(shuō)過,創(chuàng)建 Bean 的代碼是相同的,在這里,我們將統(tǒng)一介紹這一過程。

6.4.1 InstantiationAwareBeanPostProcessor

給 InstantiationAwareBeanPostProcessor 一個(gè)機(jī)會(huì)

首先,會(huì)給 InstantiationAwareBeanPostProcessor 一個(gè)機(jī)會(huì)創(chuàng)建 Bean。

	default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}

若在調(diào)用該方法時(shí)返回了一個(gè)對(duì)象,那么會(huì)將其用著參數(shù),接著調(diào)用 BeanPostProcessor 的 postProcessAfterInitialization 方法。

	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

最后,此處返回的 Object 將是最終產(chǎn)生的 Bean。 如果最終返回的 Object 不為 null 的話,那么整個(gè)創(chuàng)建 Bean 的過程就提前完成,不再執(zhí)行后面的流程。這是 bean 創(chuàng)建提前結(jié)束的機(jī)會(huì),也是我們干預(yù) bean 的創(chuàng)建機(jī)會(huì)。

6.4.2 真實(shí)地創(chuàng)建 Bean

現(xiàn)在,開始真實(shí)地創(chuàng)建 Bean 。主要過程分別是實(shí)例化,填充屬性,初始化,銷毀注冊(cè),下面簡(jiǎn)要總結(jié)這四個(gè)過程。

  • 實(shí)例化:
    • 從 BeanDefinition 提供的 Supplier 中返回;
    • 從工廠方法中返回;
    • 反射調(diào)用構(gòu)造函數(shù)返回;
      • 自動(dòng)裝配的構(gòu)造函數(shù)調(diào)用;
      • 無(wú)參構(gòu)造函數(shù)調(diào)用;

注:需要注意的是,如果這里是創(chuàng)建單例對(duì)象,那么在實(shí)例化后,屬性填充前,需要提前暴露實(shí)例化的對(duì)象到單例緩存中;

  • 填充屬性:在填充屬性前,會(huì)給 InstantiationAwareBeanPostProcessor 一個(gè)機(jī)會(huì)來(lái)修改 Bean。
  • 初始化
    • 實(shí)現(xiàn) XXXAware 相關(guān)接口的方法回調(diào);
    • 回調(diào) BeanPostProcessor.postProcessBeforeInitialization;
    • 調(diào)用初始化方法
      • InitializingBean.afterPropertiesSet
      • init-method
    • 回調(diào) BeanPostProcessor.postProcessAfterInitialization ;

注:如果這里是創(chuàng)建單例對(duì)象,在完成初始化后,需要檢測(cè)對(duì)象修改狀態(tài),這是為了解決循環(huán)依賴的。

  • 銷毀注冊(cè)
    • 檢測(cè)是否需要銷毀: 實(shí)現(xiàn)了 DisposableBean,
      AutoCloseable 接口,或者定義的內(nèi)部方法:close 或者 shutdown, 或者 自定義 DestructionAwareBeanPostProcessor 接口的實(shí)現(xiàn)類來(lái)決定是否需要消毀。
    • 單例 Bean 的銷毀:通過 DefaultSingletonBeanRegistry 中注冊(cè) DisposableBeanAdapter,并在合適的時(shí)機(jī)調(diào)用銷毀方法即可;
    • prototype 域 Bean 的銷毀:不支持,因?yàn)?Spring 不管理 prototype 域 Bean 的生命周期,自然也不會(huì)去銷毀它。
    • request 域 Bean 的銷毀: 通過 ServletRequestListener 監(jiān)聽器實(shí)現(xiàn);
    • session 域 Bean 的銷毀:通過 HttpSessionBindingListener 監(jiān)聽器實(shí)現(xiàn);
    • application 域 Bean 的銷毀:通過 ServletContextListener 監(jiān)聽器實(shí)現(xiàn);

關(guān)于如何通過監(jiān)聽器實(shí)現(xiàn),可以通過 Scope 接口的實(shí)現(xiàn)類來(lái)了解這個(gè)過程。

整個(gè) Bean 的創(chuàng)建流程便完了,在這里也只是簡(jiǎn)述,因?yàn)槲乙婚_始的目標(biāo)是不想過于復(fù)雜。但現(xiàn)在,先讓我們跳出 Bean 的創(chuàng)建流程往下走,因?yàn)槲覀冎肋€有一些“善后措施”要做。

7. 獲取對(duì)象

為什么在創(chuàng)建完 Bean 后還要獲取對(duì)象呢,因?yàn)橛?FactoryBean 的緣故,這也是我們?cè)谇拔霓D(zhuǎn)換名稱一節(jié)所提到的。

@Component
public class SessionFactoryBean implements SmartFactoryBean<SessionFactoryBean.Session> {

    @Override
    public Session getObject() throws Exception {
        return new Session("first");
    }

    @Override
    public Class<?> getObjectType() {
        return Session.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public boolean isEagerInit() {
        return true;
    }

    public static class Session{
        private String name;

        protected Session(String name){
            this.name = name;
        }

        public String getName(){
            return name;
        }
    }
}

上述代碼實(shí)現(xiàn)了 SmartFactoryBean 接口,它是一個(gè) FactoryBean。 我們?cè)谕ㄟ^名稱獲取對(duì)象時(shí):

final Object sessionFactoryBean = annotationConfigApplicationContext.getBean("sessionFactoryBean");

實(shí)際上獲取到的會(huì)是 Session 對(duì)象 ,但在容器中管理的 Bean,其實(shí)是 SessionFactoryBean 。所以,容器內(nèi)部在獲取到 Bean 后(無(wú)論是新創(chuàng)建的還是緩存的),需要在獲取對(duì)象這一步來(lái)計(jì)算出你真正想要的對(duì)象。當(dāng)然,如果你要獲取的是真正的工廠對(duì)象,通過 “&sessionFactoryBean” 名稱來(lái)獲取就好了。

8. 寫在最后

現(xiàn)在,看看我們都經(jīng)歷了什么。

首先是找到 Bean 真實(shí)的名稱,然后再嘗試從緩存,從父容器中去獲取。如果都沒有取到的話,就開始合并 BeanDefinition,檢查顯式聲明的依賴。如果都沒有問題的話,就可以開始 Bean 的創(chuàng)建了。無(wú)論是什么域的 Bean,其實(shí)創(chuàng)建代碼都一樣,只是前置后置條件不同。而 Bean 的創(chuàng)建又有四個(gè)小的過程,也可稱之為生命周期:實(shí)例化,計(jì)算屬性,初始化,bean 銷毀時(shí)的邏輯注冊(cè)。最后,Bean 無(wú)論是 從緩存中獲取成功還是創(chuàng)建成功,都需要通過計(jì)算獲取對(duì)象,因?yàn)橛锌赡芪覀儷@取到的 Bean 是一個(gè) FactoryBean,但我們實(shí)際上需要的卻是它所管理的對(duì)象。

完整的流程就在上面了。需要提醒的是在啟動(dòng)時(shí),只會(huì)完成單例 Bean (非延遲初始化)的創(chuàng)建。但如果單例 Bean 有屬性需要其它的 Bean,那么又會(huì)啟動(dòng)這些相關(guān) Bean 的創(chuàng)建過程。在顯式聲明依賴檢查時(shí),也是如此。如果當(dāng)前 Bean 有聲明依賴其它的 Bean,那么又會(huì)啟動(dòng)其它 Bean 的創(chuàng)建過程。

Bean 就是在這樣一個(gè)循環(huán)往復(fù)的過程中被創(chuàng)建,并在 Spring 的管理下生生不息的。

到此這篇關(guān)于Spring中Bean的創(chuàng)建流程詳細(xì)解讀的文章就介紹到這了,更多相關(guān)Spring中Bean的創(chuàng)建流程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java定義數(shù)組的三種類型總結(jié)

    java定義數(shù)組的三種類型總結(jié)

    下面小編就為大家?guī)?lái)一篇java定義數(shù)組的三種類型總結(jié)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2016-10-10
  • spring中actuator監(jiān)視器配置詳解

    spring中actuator監(jiān)視器配置詳解

    這篇文章主要介紹了spring中actuator監(jiān)視器配置詳解,actuator主要是完成微服務(wù)的監(jiān)控,完成監(jiān)控治理,可以查看微服務(wù)間的數(shù)據(jù)處理和調(diào)用,當(dāng)它們之間出現(xiàn)了異常,就可以快速定位到出現(xiàn)問題的地方,需要的朋友可以參考下
    2023-09-09
  • 詳解使用spring boot admin監(jiān)控spring cloud應(yīng)用程序

    詳解使用spring boot admin監(jiān)控spring cloud應(yīng)用程序

    本篇文章主要介紹了詳解使用spring boot admin監(jiān)控spring cloud應(yīng)用程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Spring入門實(shí)戰(zhàn)之Profile詳解

    Spring入門實(shí)戰(zhàn)之Profile詳解

    什么是spring profile?簡(jiǎn)單講profile就是一組配置,不同profile提供不同組合的配置,程序運(yùn)行時(shí)可以選擇使用哪些profile來(lái)適應(yīng)環(huán)境。下面這篇文章主要介紹了Spring中Profile實(shí)戰(zhàn)的相關(guān)資料,需要的朋友可以參考借鑒。
    2017-02-02
  • SWT(JFace)體驗(yàn)之打開多個(gè)Form

    SWT(JFace)體驗(yàn)之打開多個(gè)Form

    SWT(JFace)體驗(yàn)之打開多個(gè)Form的實(shí)現(xiàn)代碼。
    2009-06-06
  • 從內(nèi)存地址解析Java的static關(guān)鍵字的作用

    從內(nèi)存地址解析Java的static關(guān)鍵字的作用

    這篇文章主要介紹了從內(nèi)存地址解析Java的static關(guān)鍵字的作用,包括靜態(tài)成員變量和靜態(tài)方法等重要內(nèi)容,需要的朋友可以參考下
    2015-10-10
  • spring+maven實(shí)現(xiàn)發(fā)送郵件功能

    spring+maven實(shí)現(xiàn)發(fā)送郵件功能

    這篇文章主要為大家詳細(xì)介紹了spring+maven實(shí)現(xiàn)發(fā)送郵件功能,利用spring提供的郵件工具來(lái)發(fā)送郵件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • 深入理解Java中Filter的作用種類及應(yīng)用場(chǎng)景

    深入理解Java中Filter的作用種類及應(yīng)用場(chǎng)景

    Filter(過濾器)是Java Web中的一種重要組件,可以對(duì)請(qǐng)求和響應(yīng)進(jìn)行攔截處理,對(duì)數(shù)據(jù)進(jìn)行過濾和處理。Filter可以實(shí)現(xiàn)許多功能,如:鑒權(quán)、日志記錄、字符編碼轉(zhuǎn)換、數(shù)據(jù)壓縮、請(qǐng)求重定向等等
    2023-04-04
  • Java調(diào)用groovy實(shí)現(xiàn)原理代碼實(shí)例

    Java調(diào)用groovy實(shí)現(xiàn)原理代碼實(shí)例

    這篇文章主要介紹了Java調(diào)用groovy實(shí)現(xiàn)原理代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-12-12
  • 實(shí)例講解Java設(shè)計(jì)模式編程中的OCP開閉原則

    實(shí)例講解Java設(shè)計(jì)模式編程中的OCP開閉原則

    這篇文章主要介紹了Java設(shè)計(jì)模式編程中的開閉原則,開閉原則的大意被作者總結(jié)為用抽象構(gòu)建框架,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié),需要的朋友可以參考下
    2016-02-02

最新評(píng)論

安徽省| 海兴县| 怀化市| 龙游县| 玛纳斯县| 定日县| 灯塔市| 土默特右旗| 商洛市| 屯门区| 陵水| 肃北| 平乡县| 青铜峡市| 仁寿县| 毕节市| 全椒县| 桐城市| 邯郸市| 武冈市| 桃园市| 黄平县| 金山区| 绥宁县| 阿拉尔市| 元江| 昌宁县| 海伦市| 福泉市| 如东县| 阳江市| 福鼎市| 扎囊县| 花莲县| 迁西县| 木兰县| 峨眉山市| 吉木萨尔县| 佳木斯市| 汉寿县| 台江县|