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

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

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

前言

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

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

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

看見這一步,或許會有所疑惑,但這確實是第一步,因為我們需要一把“鑰匙”。這一步是將我們獲取 Bean 時所指定的名稱,轉(zhuǎn)換為 Spring 容器中所管理 Bean 的真實名稱。

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

或者,在我們實現(xiàn) FactoryBean 接口以期待通過工廠獲取 Bean 時。在想要獲取真正的工廠類而不是所管理的 Bean ,這一步則會去掉前綴 &,因為在容器中存儲的工廠 Bean 的真實名稱是沒有前綴的。但如果不去掉前綴,則獲取到的是工廠所管理的 Bean,這在后文有案例。

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

2. 從三級緩存中獲取

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

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

3. 從父容器中獲取

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

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

4. 合并 BeanDefinition

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

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

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

5. 檢查依賴

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

6. 創(chuàng)建

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

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

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

6.1 單例 Bean

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

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

6.2 原型 Bean

因為原型每次都會創(chuàng)建對象,所以不會存在從緩存中去獲取。但這里仍然需要做循環(huán)依賴的檢查,并且原型 Bean 間的循環(huán)依賴無論如何,都無法解決的。我們可以在腦海中仔細想一想,是不是這個道理。

6.3 其它域 Bean

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

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

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

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

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

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

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

6.4.1 InstantiationAwareBeanPostProcessor

給 InstantiationAwareBeanPostProcessor 一個機會

首先,會給 InstantiationAwareBeanPostProcessor 一個機會創(chuàng)建 Bean。

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

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

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

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

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

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

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

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

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

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

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

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

整個 Bean 的創(chuàng)建流程便完了,在這里也只是簡述,因為我一開始的目標(biāo)是不想過于復(fù)雜。但現(xiàn)在,先讓我們跳出 Bean 的創(chuàng)建流程往下走,因為我們知道還有一些“善后措施”要做。

7. 獲取對象

為什么在創(chuàng)建完 Bean 后還要獲取對象呢,因為有 FactoryBean 的緣故,這也是我們在前文轉(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;
        }
    }
}

上述代碼實現(xiàn)了 SmartFactoryBean 接口,它是一個 FactoryBean。 我們在通過名稱獲取對象時:

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

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

8. 寫在最后

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

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

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

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

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

相關(guān)文章

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

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

    下面小編就為大家?guī)硪黄猨ava定義數(shù)組的三種類型總結(jié)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    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)用程序,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Spring入門實戰(zhàn)之Profile詳解

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

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

    SWT(JFace)體驗之打開多個Form

    SWT(JFace)體驗之打開多個Form的實現(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實現(xiàn)發(fā)送郵件功能

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

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

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

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

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

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

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

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

最新評論

绥阳县| 理塘县| 诏安县| 长武县| 乌苏市| 随州市| 福州市| 汨罗市| 南木林县| 苏尼特右旗| 泽库县| 德格县| 佛坪县| 浦城县| 克什克腾旗| 碌曲县| 莱州市| 巴青县| 潞西市| 乌拉特前旗| 荣成市| 合阳县| 五大连池市| 靖远县| 黎城县| 和静县| 双桥区| 元谋县| 庆城县| 延川县| 阿克苏市| 五常市| 汉中市| 宣化县| 永福县| 巩义市| 鱼台县| 临猗县| 山丹县| 景东| 会宁县|