欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2023年10月30日 09:08:16   作者:荊軻刺秦  
這篇文章主要介紹了Spring中容器的創(chuàng)建流程詳細解讀,Spring?框架其本質(zhì)是作為一個容器,提供給應(yīng)用程序需要的對象,了解容器的誕生過程,有助于我們理解?Spring?框架,也便于我們“插手”這個過程,需要的朋友可以參考下

前言

在容器準備階段,需要告知容器從哪里讀取 Bean 信息以及環(huán)境信息。盡管,有時我們并未提供這些信息,但容器依然能正確創(chuàng)建。這得益于一些默認行為(約定大于配置),所以,也不需要由我們主動告知。

1. Bean 的準備

Bean 的準備階段離不開 BeanDefinationRegistry 和 BeanFactory。

這里需要明白 BeanDefination 對象代表的是 Bean 的描述信息。Bean 并不是從一開始就實例化放入容器的。相反,最開始容器只是擁有它的描述信息,所以我們將此階段稱為 Bean 的準備階段。根據(jù)這些描述信息,Spring 能夠創(chuàng)建 Bean,并使它更“完整”。

BeanDefination 在 Spring 中的地位類似于我們的 Class 對象在 JVM 中的地位。

1.1 BeanDefinationRegistry

容器是通過 BeanDefinationRegistry 來注冊 BeanDefination 的。 我們可以通過 xml 文件或者 Java 配置的方式來聲明 BeanDefination,并在創(chuàng)建容器的時候指定 xml 文件或者 Java 類,然后,容器會掃描并注冊 BeanDefination。

那么,此階段我們的機會在哪里呢?其實,Spring 已經(jīng)包含了足夠完善的從定義的 Bean 信息到 BeanDefination 注冊。但如果你仍然想自定義 BeanDefination,或者修改注冊的 BeanDefination ,可以通過獲取 BeanDefinationRegistry 對象。

Spring 通過回調(diào)的方式,調(diào)用實現(xiàn) BeanDefinitionRegistryPostProcessor 接口的類,所以,此類交由 Spring 管理。該類將在后面介紹的刷新階段回調(diào),此為機會一。

Spring 自身也定義了實現(xiàn) BeanDefinitionRegistryPostProcessor 接口的類來完成和我們一樣想要在運行時自定義加載 BeanDefination 的功能,像 ConfigurationClassPostProcessor,調(diào)用時機和我們自定義的一樣,被調(diào)用的順序依賴實現(xiàn) PriorityOrdered 或 Order 接口。

平等對待第三方,在 dubbo 中見過這種說法,或許對于框架開發(fā),這種做法一直很重要。

1.2 BeanFactory

那么,容器中持有的 Bean 一定需要對應(yīng)的 BeanDefination 嘛?這不一定,我們可以直接通過 BeanFactory 來注冊實例化對象。Spring 在將一些環(huán)境信息對象放入容器中,也是這樣做的。

此時,我們的機會又在哪里呢?兩種方式:

  • 實現(xiàn) BeanFactoryAware 接口。
  • 實現(xiàn) BeanFactoryPostProcessor 接口。

實現(xiàn)用于拓展的接口的類都必須交由 Spring 管理才能觸發(fā)回調(diào),此為機會二。上述兩個回調(diào)的時機也都在刷新階段,后續(xù)將詳細敘述。

先做個小結(jié),現(xiàn)在,我們知道了,我們可以通過 BeanDefinationRegistry 操作 BeanDefination,通過 BeanFactory 操作 Bean,那么,如何獲得這兩個對象便是我們的機會所在。

環(huán)境信息

環(huán)境信息包括 profile 和 property,前者可以看作對環(huán)境信息做的分類,因為我們需要在不同時候,激活不同的環(huán)境,像生產(chǎn)和開發(fā)環(huán)境。后者是屬性,屬性可來自不同的屬性源。并且,通過環(huán)境信息,我們可以將 ${} 占位符替換為具體的屬性值。

那么,此時的機會在哪里呢?我們可以自定義一個實現(xiàn) EnvironmentAware 接口的類,此為機會三。

2. 刷新

刷新階段涉及到很多回調(diào),我們將一一分析。

2.1 調(diào)用 BeanFactoryPostProcessors

此階段包含了 BeanDefinitionRegistryPostProcessor 接口的回調(diào),因為 BeanDefinitionRegistryPostProcessor 接口繼承自 BeanFactoryPostProcessor。

調(diào)用順序為:

  • 調(diào)用 BeanDefinitionRegistryPostProcessor;
  • 調(diào)用 BeanFactoryPostProcessor;實現(xiàn)相同接口的對象之間調(diào)用順序?qū)凑?nbsp;PriorityOrdered 和 Ordered 接口來。

2.2 注冊 BeanPostProcessor

BeanPostProcessor 作用于對象初始化前后,我們實現(xiàn)這個接口便可以在對象初始化前后做處理。同樣,實現(xiàn)了該接口后,需要交由 Spring 管理就好。 此階段僅僅是將 BeanPostProcessor 注冊到 BeanFactory 中,并不調(diào)用。注冊順序為:

  1. 實現(xiàn)了 PriorityOrdered 的 BeanPostProcessor;
  2. 實現(xiàn)了 Ordered 的 BeanPostProcessor;
  3. 僅實現(xiàn) BeanPostProcessor;
  4. 實現(xiàn)了 MergedBeanDefinitionPostProcessor

MergedBeanDefinitionPostProcessor 繼承自 BeanPostProcessor。

2.3 實例化所有非延遲實例化的單例

在實例化單例 Bean 時,第一個回調(diào)機會給了 InstantiationAwareBeanPostProcessor 接口,該接口的 postProcessBeforeInstantiation() 方法如果返回了一個對象,該對象將用于存入容器,此為機會四。

如果上面的接口未返回對象,則還允許我們在根據(jù) BeanDefination 創(chuàng)建對象前,再修改 BeanDefination。第二個回調(diào)機會便給了 MergedBeanDefinitionPostProcessor 接口的 postProcessMergedBeanDefinition() 方法。此為機會五。

還有,在存在循環(huán)依賴的時候, 如果你想修改你已經(jīng)放入容器的對象,可以實現(xiàn) SmartInstantiationAwareBeanPostProcessor 接口,該接口的 getEarlyBeanReference() 方法將被調(diào)用。此接口的使用能夠保證其它對象依賴注入的是你修改后的對象,容器中也是你修改后的對象。

比如說 A 依賴了 B,B 依賴了 A。在檢測到 A 依賴 B 的時候,A 已經(jīng)實例化完成,這時候該去走創(chuàng)建B 的過程,然后在創(chuàng)建 B 的過程,發(fā)現(xiàn) A 已經(jīng)創(chuàng)建完成,可以依賴注入,B 的過程正常結(jié)束?;氐絼?chuàng)建 A 的過程,這時,A 也正常注入 B,循環(huán)依賴過程完成了。若此時,你再通過 BeanPostProcessor 重新實例化了對象 A 放入容器,這就會導(dǎo)致容器中的 A 和 B 依賴的 A 不是同一個對象。這違背了單例原則,所以,我們可以通過上述描述的接口來保證。

緊接著的回調(diào)機會給了 InstantiationAwareBeanPostProcessor 接口的 postProcessAfterInstantiation() 方法,此時還未給屬性賦值,但我們可以操作實例化的對象了。

再后來,給了我們修改屬性值的機會,回調(diào) InstantiationAwareBeanPostProcessor 接口的 postProcessProperties() 方法。如果此方法返回 null ,還可再回調(diào) postProcessPropertyValues 方法。

接下來的過程就是初始化了。初始化之前,如果 Bean 實現(xiàn)了以下接口,那么會回調(diào)接口對應(yīng)的方法:

  • BeanNameAware;
  • BeanClassLoaderAware;
  • BeanFactoryAware;

然后,再回調(diào) BeanPostProcessor 的 postProcessBeforeInitialization 方法。 其實,關(guān)于其它 Aware 接口的回調(diào),便是通過 ApplicationContextAwareProcessor 來做的,它是一個 BeanPostProcessor。所以,如果你自定義了一個 Bean,并且實現(xiàn)了 PriorityOrdered 接口,如果它的優(yōu)先級過高,那么它可能在 ApplicationContextAwareProcessor 之前調(diào)用,這樣你某些實現(xiàn)了 Aware 接口的 Bean,可能還尚未獲取 aware 注入的對象。

緊接著,就是調(diào)用 Bean 的初始化方法,實現(xiàn)了 InitializingBean 的 afterPropertiesSet() 方法或者指定的 init-method 方法將被調(diào)用。

初始化完后,便是 BeanPostProcessor 的 postProcessAfterInitialization() 的方法調(diào)用。

至此,容器創(chuàng)建過程的回調(diào)就介紹完了。

3. 寫在最后

我們的機會在于 BeanDefinationRegistry、BeanFactory 以及處理 Bean。需要注意各個回調(diào)接口的調(diào)用時機以及條件,這里整理下具體的回調(diào)順序如下:

  • 前期(BeanFactoryPostProcessor)
    • 處理 BeanDefination: BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry();
    • 處理 BeanFactory: BeanFactoryPostProcessor.postProcessBeanFactory();
  • 創(chuàng)建 Bean(BeanPostProcessor)
    • 自定義實例化:InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(); 自定義實例化過程有很多實現(xiàn)方式,比如 FactoryBean 或者提供一個 Supplier;
    • 實例化前修改 BeanDefination: MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition() ;
    • 實例化后,屬性賦值前:InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
    • 修改屬性(PropertyValue):InstantiationAwareBeanPostProcessor.postProcessProperties(),如果該方法返回 null,還有可以通過 postProcessPropertyValues() 方法來修改。
    • 屬性賦值。
    • 初始化前:BeanNameAware,BeanClassLoaderAware,BeanFactoryAware,BeanPostProcessor.postProcessBeforeInitialization()。
    • 初始化:InitializingBean.afterPropertiesSet() 或者 init-method。
    • 初始化后:BeanPostProcessor.postProcessAfterInitialization()。

梳理完上述過程,再結(jié)合我們自己的需要,就知道我們?nèi)绾瓮卣沽恕?/p>

一個容器的誕生,這個名字聽起來很舒服,這讓我想到一部電影-傳奇的誕生。盡管我所寫的是再平凡不過,但我仍愿為此賦予一個美好的寓意。

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

相關(guān)文章

最新評論