本節介紹神祕的IOC容器,探究IOC是怎樣工作的?Bean創建的時機是怎樣的?以及Spring是如何解決循環bean依賴的?當了解這些之後,遇到Spring報的一些錯就不會無裏頭了。俗話說一圖勝似千言萬語,先上圖, 依據自己對源碼的分析和了解大概將IOC的知識點分爲這幾塊。
<0>IOC容器涉及知識點
在進入正文之前大概先對以上六點做一個整體介紹,認識一下它們是怎麼回事。
1、Bean生命週期:Bean的實例化-->Bean的初始化(初始化方法、屬性賦值)-->Bean的使用-->bean銷燬。
注:標註紅色五角星是bean實例化流程中最重要的三部分,將會在getBean()流程中詳細講解。
<1>Bean的實例化流程圖
2、循環依賴:
- 構造器循環依賴 ---- 無解,將拋出BeanCurrentlyInCreationException異常表示循環依賴
- field屬性的循環依賴
- setter循環依賴 ---- spring可以通過提前曝光進行解決)
- prototype作用域bean的循環依賴 ---- 因爲不做緩存所以無解
Spring怎麼解決循環依賴?
①singletonFactories 、earlySingletonObjects、singletonObjects三級緩存。
②創建Bean的時候會提前曝光,在屬性賦值的時候,會先將通過構造器創建出來的(還沒初始化,屬性值還都是null)bean給暴露出來,給屬性引用。
3、常用擴展點(前置後置增強):
InstantiationAwareBeanPostProcessor: 實例化前後 postProcessBeforeInstantiation() postProcessAfterInstantiation() BeanPostProcessor: 初始化前後 postProcessBeforeInitialization() postProcessAfterInitialization() 區別:Instantiation表示實例化,Initialization表示初始化。實例化的意思在對象還未生成,初始化的意思在對象已經生成。
InstantiationAwareBeanPostProcessor接口的主要作用在於目標對象的實例化過程中需要處理的事情,包括實例化對象的前後過程以及實例的屬性設置;BeanPostProcessor接口允許在調用初始化方法前後對Bean進行額外的處理。Bean後置處理器對IOC容器裏的所有Bean實例逐一處理,而非單一實例。其典型應用是:檢查Bean屬性的正確性或根據特定的標準更改Bean的屬性。
4、BeanFactory和ApplicationContext前世今生:
spring的兩種容器:a、BeanFactoy
b、ApplicationContext應用上下文ApplicationContext和BeanFactory都是用於加載 bean 的,但是ApplicationContext提供了更多的功能,包含了BeanFactory的所有功能。
BeanFactory:BeanhFactory使用延遲加載所有的Bean,爲了從BeanhFactory得到一個Bean,只要調用getBean()方法,就能獲得Bean。
ApplicationContext除了提供上述BeanFactory所能提供的功能之外,還提供了更完整的框架功能:
a. 國際化支持
b. 資源訪問:Resource rs = ctx. getResource(“classpath:config.properties”), “file:c:/config.properties”
c. 事件傳遞:通過實現ApplicationContextAware接口
5、無所不知的Aware:
Aware是一個具有標識作用的超級接口,實現該接口的bean是具有被spring 容器通知的能力的,而被通知的方式就是通過回調。也就是說:直接或間接實現了這個接口的類,都具有被spring容器通知的能力。
《1、Bean生命週期》中的介紹的第3步檢查Aware接口並設置相關依賴 ----- invokeAwareMethods(),則是通過回調設置beanName。例如實現了BeanNameAware則具備獲取獲得到容器中Bean的名稱的能力。
6、Context的初始化過程:
context的出初始化依舊離不開接口ApplicationContext;AbstractApplicationContext是它的抽象基礎類,無論是XML文件配置的還是基於註解配置的Bean,其最終都是真正從下圖開始,只不過是前期BeanDefinition的處理不同而已。
<2>Context的初始化主要步驟圖
正文:
IOC容器:IOC全程爲Inversion of Control,即控制反轉,就是由 Spring IOC 容器來負責對象的生命週期和對象之間的關係。白話不多說僅此一句就夠了。下面來自讓我們真正進入spring內部,瞭解IOC的思想以及技術點。
目錄
目錄
五、BeanFactory和ApplicationContext前世今生:
一、前期認識:
剛開始我們先介紹下關鍵的幾個類以及它們各自扮演的角色和關係。
- DefaultListableBeanFactory :: preInstantiateSingletons()----->getBean()context初始化時獲取bean入口
DefaultListableBeanFactory是整個bean加載的核心部分,是Spring註冊及加載bean的默認實現(即所有的BeanDefinition就是註冊在此類的map中),它是一個基於bean定義對象的成熟bean工廠,典型的用法是在訪問beans之前(即項目啓動的時候)註冊所有的Bean;
而XmlBeanFactory對DefaultListableBeanFactory類進行了擴展,主要用於從XML文檔中讀取BeanDefinition,對於註冊及獲取Bean都是使用從父類DefaultListableBeanFactory繼承的方法去實現,而唯獨與父類不同的個性化實現就是增加了XmlBeanDefinitionReader類型的reader屬性。
- AbstractBeanFactory :: doGetBean()真正獲取bean的方法
AbstractBeanFactory 繼承DefaultSingletonBeanRegistry並實現了ConfigurableBeanFactory,因此該類具有管理、註冊、緩存單例bean能力,並且增加了對FactoryBean的特殊處理功能。
- DefaultSingletonBeanRegistry :: getSingleton()獲取單例bean
它繼承SimpleAliasRegistry類和實現了SingletonBeanRegistry接口,因此這個類可以有別名註冊的功能和單例bean註冊的功能(緩存單例bean--map),並且還支持註冊DisposableBean實例;它依賴ObjectFactory接口和DisposableBean接口(關閉註冊表時調用到了destroy方法)。
- AbstractAutowireCapableBeanFactory :: doCreateBean()創建bean實例
綜合AbstractBeanFactory並對接口AutowireCapableBeanFactory進行實現。供bean的創建 (有construct方法), 屬性注值, 綁定 (包括自動綁定)和初始化。處理運行時bean引用, 解析管理的集合, 調用初始化方法。提供通過屬性name、屬性type以及構造器的注入
圖<1-1>創建bean關鍵類圖
二、Bean 的生命週期:
1、bean的作用域:
singleton(缺省作用域) | 在整個應用中,只創建bean的一個實例,在IOC容器中共享,容器創建的時候就實例化了這個bean |
prototype |
每次注入或者通過Spring應用上下文獲取的時候,都會創建一個新的bean實例,相當於每次都new bean(),容器創建的時候沒有實例化了bean,而是在請求獲取的時候纔會創建對象 |
request |
每次http請求都會創建一個bean實例,這個bean實例只在當前request請求內有效,請求結束的時候,這個bean實例被銷燬;該作用域僅適用於WebApplicationContext環境 |
session | 同一個http session共享一個bean實例,不同的Session使用不同的Bean,該作用域僅適用於WebApplicationContext環境 |
globalSession | 一般用於Prolet應用環境,該作用域僅適用於WebApplicationContext環境 |
2、生命週期從Bean實例化開始
概述的圖<1>中便是Bean實例化的大致流程圖,實例化所涉及的關鍵類的類圖如圖<1-1>注意黃色註釋的四個類:
- DefaultListableBeanFactory :: preInstantiateSingletons()----->getBean()獲取bean的地方
- AbstractBeanFactory :: doGetBean()真正獲取bean的方法
- DefaultSingletonBeanRegistry :: getSingleton()獲取單例bean
- AbstractAutowireCapableBeanFactory :: doCreateBean()創建bean實例
在深入源碼之前綜合概述中的流程圖,先宏觀分析對源碼做個大概的瞭解:
圖<2-1>bean實例化流程分析
讀過這個圖,大家可能對第二步-->第三步有點疑問,ObjectFactory接口類的getObject()爲什麼會直接調用createBean(...)方法,其實ObjectFactory是一個函數式接口,調用getObject()時,其實真正回去回調getSIngleton(...)參數中的函數表達式()->createBean(...)。
通過宏觀分析,讓我們對Bean的實例化過程有一個大概的思路,下面開始深入源碼一步步解讀:
首先在正式分析createBean(String, RootBeanDefinition, Object[])
方法前,我們先來看看 createBean 方法是在哪裏被調用的(如下圖代碼)。doGetBean 方法的代碼片段,從中可以發現 createBean 方法被匿名工廠類ObjectFactory的 getObject 方法包裹,但這個匿名工廠類對象並未直接調用 getObject 方法。而是將自身作爲參數傳給了getSingleton(String, ObjectFactory)
方法,createBean ()真正是在getSingleton()方法中回調的。
AbstractBeanFactory::
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// ...省略...
/********* 1、先從緩存中取 **********/
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// ...省略...
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
/********* 2、緩存中中沒有就創建 **********/
else {
/********* 2.1、 如果是原型bean並且正在被創建,則報異常(循環依賴會在第三部分介紹)**********/
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
/********* 2.2、標記正在被創建 **********/
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
/********* 2.2、單例bean開始創建**********/
// Create bean instance.
if (mbd.isSingleton()) {
/********* 2.3、開始獲取單例bean*********/
sharedInstance = getSingleton(beanName, () -> {
try {
/********* 2.4 createBean 方法被匿名工廠類的 getObject 方法包裹,
但這個匿名工廠類對象並未直接調用 getObject方法,而是將自
身作爲參數傳給了2.3步的getSingleton(String, ObjectFactory)
方法*********/
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
return (T) bean;
}
doGetBean 方法的大概處理步驟如下(代碼中有每一步相關注釋):
1. 先從緩存中取,取緩存順序依次是singletonObjects-->earlySingletonObjects-->singletonFactories,取到就直接返回。
2. 若爲空,如果是原型bean並且正在被創建,則報異常
3. 若是單例bean,則標記正在被創建
4. 開始創建單例Bean,createBean 方法被匿名工廠類的 getObject 方法包裹,但這個匿名工廠類對象並未直接調用
getObject 方法,而是在getSingleton(String, ObjectFactory
)方法中回調。代碼中註釋的2.1也解釋了prototype作用域bean的循環依賴Spring是無助的。
從上面代碼分析createBean()是在getSingleton(String, ObjectFactory)方法中回調的,我們來具體分析下此方法
是如何處理的。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) {
// 1、從緩存中獲取單例 bean,若不爲空,則直接返回,不用再創建
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// ....省略....
/*
* 2、將 beanName 添加到 singletonsCurrentlyInCreation 集合中,
* 用於表明 beanName 對應的 bean 正在創建中(表明單例bean正在創建中)
*/
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<Exception>();
}
try {
/*****3、通過 getObject 方法調用 createBean 方法創建 bean 實例***/
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// ....省略....
}
catch (BeanCreationException ex) {
// ....省略....
}
finally {
/***4、 將 beanName 從 singletonsCurrentlyInCreation 移除***/
afterSingletonCreation(beanName);
}
if (newSingleton) {
/*
* 5、將 <beanName, singletonObject> 鍵值對添加到 singletonObjects 集合中,
* 並從其他集合(比如 earlySingletonObjects)中移除 singletonObject 記錄
*/
addSingleton(beanName, singletonObject);
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
getSingleton(String, ObjectFactory)只看關鍵代碼其
邏輯不是很複雜,大概處理邏輯如下(代碼中每一步也有相關注釋):
- 先從 singletonObjects 集合獲取 bean 實例,若不爲空,則直接返回
- 若爲空,進入創建 bean 實例階段。先將 beanName 添加到 singletonsCurrentlyInCreation
- 通過 getObject 方法回調
getSingleton(String, ObjectFactory)的入參
createBean() 方法創建 bean 實例- 無論是否創建成功最終都會將 beanName 從 singletonsCurrentlyInCreation 集合中移除
- 將 <beanName, singletonObject> 映射緩存到 singletonObjects (緩存所有的單實例bean)集合中
從上面的分析中,我們知道了 createBean 方法在何處被調用的。那麼接下來我們一起重點深入 createBean 方法的源碼中,來看看這個方法具體都做了什麼事情。
AbstractAutowireCapableBeanFactory::
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
RootBeanDefinition mbdToUse = mbd;
// ... 省略...
try {
/**** 1、Give BeanPostProcessors a chance to return a proxy instead of the
*** target bean instance.(實例化前後置處理,如果不爲空則直接返回,一般bean創建
*** 都爲空)
*** 問題:爲什麼spring是在初始化之後proxy而不是resolveBeforeInstantiation
*** resolveBeforeInstantiation只是針對有自定義的targetsource,因爲自定義的
*** targetsource不是spring的bean那麼肯定不需要進行後續的一系列的實例化 初始化。所以
*** 可以在resolveBeforeInstantiation直接進行proxy
***/
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
/****2、開始真正創建bean******/
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
createBean 方法處理較爲簡單,主要是有一個預處理-----後置處理器處理(代碼中的第1步),當然對於一般的bean處理結果都返回空;然後真正創建bean的邏輯是在doCreateBean()方法中;這個doxxx()是Spring框架中經常用的,往往在做某件事情之前都會做一些預備工作,最終的處理邏輯在doxxx()方法中。
AbstractAutowireCapableBeanFactory::
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
/*
* BeanWrapper 是一個基礎接口,由接口名可看出這個接口的實現類用於包裹 bean 實例。
* 通過 BeanWrapper 的實現類可以方便的設置/獲取 bean 實例的屬性
*/
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
/*
* 1.反射創建 bean 實例,並將實例包裹在 BeanWrapper 實現類對象中返回。
* createBeanInstance 中包含三種創建 bean 實例的方式:
* ①. 通過工廠方法創建 bean 實例
* ②. 通過構造方法自動注入(autowire by constructor)的方式創建 bean 實例
* ③. 通過無參構造方法方法創建 bean 實例
*
* 注意:若 bean 的配置信息中配置了 lookup-method 和 replace-method,則會使用
* CGLIB增強 bean 實例。(可以先不關注,只關注主流程創建bean實例)
*/
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
/*** 補充:此處創建的bean是一個原始的bean,並沒有任何屬性填充和初始化。**/
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// ...省略...
/*
* 2、(解決循環依賴)earlySingletonExposure 是一個重要的變量,這裏要說明一下。該變量用於表
* 示是否提前暴露單例 bean,用於解決循環依賴。切記:此處的bean是原始bean,並沒有屬性賦值和初
* 始化,因爲是引用,所以bean初始化完成之後就會變成完整的bean。
* earlySingletonExposure 由三個條件綜合而成,如下:
* 條件1:mbd.isSingleton() - 表示 bean 是否是單例類型
* 條件2:allowCircularReferences - 是否允許循環依賴
* 條件3:isSingletonCurrentlyInCreation(beanName) - 當前 bean 是否處於創建的狀態中
*
* earlySingletonExposure = 條件1 && 條件2 && 條件3
* = 單例 && 是否允許循環依賴 && 是否存於創建狀態中。
*/
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
/** 補充:獲取獲取原始對象早期 bean 的引用,如果 bean 中的方法被 AOP 切點所匹配
** 到,在 getEarlyBeanReference 方法中,會執行 AOP 相關邏輯。
** 若 bean 未被 AOP 攔截,getEarlyBeanReference 原樣返回
** bean,所以大家可以把
** return getEarlyBeanReference(beanName, mbd, bean)
** 等價於:
** return bean;
**/
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
/*** 3、屬性賦值,稍後專門分析***/
populateBean(beanName, mbd, instanceWrapper);
/*
* 4、進行餘下的初始化工作,詳細如下:
* ①. 判斷 bean 是否實現了 BeanNameAware、BeanFactoryAware、
* BeanClassLoaderAware 等接口,並執行接口方法
* ②. 應用 bean 初始化前置操作
* ③. 如果 bean 實現了 InitializingBean 接口,則執行 afterPropertiesSet
* 方法。如果用戶配置了 init-method,則調用相關方法執行自定義初始化邏輯
* ④. 應用 bean 初始化後置操作
*
* 另外,AOP 相關邏輯也會在該方法中織入切面邏輯,此時的 exposedObject 就變成了
* 一個代理對象了
*/
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
}
// ...省略...
// Register bean as disposable.
try {
/**** 5、註冊銷燬邏輯***/
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
}
return exposedObject;
}
三、循環依賴:
依據第二節的Bean實例化步驟分析,我們來做一個簡單的流程分析:
1. 創建原始 bean 實例 → createBeanInstance(beanName, mbd, args)
2. 添加原始對象工廠對象到 singletonFactories 緩存中 → addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
3. 填充屬性,解析依賴 → populateBean(beanName, mbd, instanceWrapper)重點來了,addSingletonFactory()就是解決循環依賴的關鍵點,因爲此方法可以將原始對象的引用提早加入緩存以進行曝光。舉個例子,假設 ClassA和ClassB相互依賴,那麼實例化ClassA時通過方法addSingletonFactory()先將自己的原始Bean加入緩存提前暴露引用,然後在調用populateBean()進行屬性填充時,發現依賴ClassB,然後就會首先實例化ClassB;ClassB當遇到這個方法時也會解析自己的依賴,然後對於ClassA這個依賴,會在getSington(String beanName)時獲取到早期暴露的原始bean,獲取到依賴的早期bean之後,ClassB就可以完成實例化工作,然後ClassA就可以獲取到完整的ClassB實例,最後再會退回來繼續實例化ClassA,當Class完成實例化之後ClassA和ClassB之間就完成了完整的bean依賴也處於可用狀態。
getSington(String beanName)具體源碼如下,源碼中提到了獲取bean時的幾個緩存,同時在概述的第2節我們也提到了這幾個緩存,下面詳細介紹下這幾個緩存:
緩存 容器 作用 singletonObjects Map(key=beanName,value=bean實例) 用於存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用 earlySingletonObjects Map 存放原始的 bean 對象(尚未填充屬性),用於解決循環依賴 singletonFactories Map 存放 bean 工廠對象,用於解決循環依賴 getSington(String beanName)是在doGetBean()方法中調用的(可以看上一節創建bean實例源碼分析),從代碼中分析邏輯很簡單:
1. 首先從 singletonObjects 緩存中獲取 bean 實例。
2. 若未命中,再去 earlySingletonObjects 緩存中獲取原始 bean 實例。
3. 如果仍未命中,則從 singletonFactory 緩存中獲取 ObjectFactory 對象,然後再調用 getObject 方法獲取原始 bean 實例的應用,也就是早期引用。
4. 獲取成功後,將該實例放入 earlySingletonObjects 緩存中,並將 ObjectFactory 對象從 singletonFactories 移除。
DefaultSingletonBeanRegistry::
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
/**** 從緩存中拿到早期原始bean *****/
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
最後一張圖來結束這一小節:
四、常用擴展點(前置後置增強):
依舊是先上圖讓我們對bean的增強器的作用點有大概的宏觀瞭解,如下圖所示,其實簡單理解就是:
bean實例化前後分別會由InstantiationAwareBeanPostProcessor的前置和後置處理方法去處理,而在初始化前後會由BeanPostProcessor的前置和後置處理方法處理。
注意區別:InstantiationAwareBeanPostProcessor的兩個方法是由Instantiation結尾,表明是實例化專用;BeanPostProcessor的兩個方法是由Initialization結尾表明是初始化專用。
圖<4-1>處理器在bean創建流程中的插入點
下面根據上圖以及源碼來詳細分析,有部分源碼可能和前面有重複,所以只關注本節要講述的源碼,
1、InstantiationAwareBeanPostProcessor前置代碼分析:
AbstractAutowireCapableBeanFactory::
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// ...省略...
try {
/**** 1、Give BeanPostProcessors a chance to return a proxy instead of the
*** target bean instance.(實例化前後置處理,如果不爲空則直接返回,一般bean創建
*** 都爲空)
*** 繼續跟進方法內部分析
***/
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
}
try {
/** 2、前置處理結束,開始創建bean **/
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
}
catch (Throwable ex) {
}
}
---------------------------------------------------------------------------------
AbstractAutowireCapableBeanFactory::
/** 在 resolveBeforeInstantiation 方法中,當前置處理方法返回的 bean 不爲空時,後置處理纔會被執
* 行。前置處理器是 InstantiationAwareBeanPostProcessor 類型的,該種類型的處理器一般用在 Spring
* 框架內。
*** 問題:爲什麼spring是在初始化之後proxy而不是resolveBeforeInstantiation
*** resolveBeforeInstantiation只是針對有自定義的targetsource,因爲自定義的
*** targetsource不是spring的bean那麼肯定不需要進行後續的一系列的實例化 初始化。所以
*** 可以在resolveBeforeInstantiation直接進行proxy
**/
@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
/*
* 一般單例bean在這邊處理都是返回null,這邊不是重點,一般bean的創建這邊都會返
* 回null
*/
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
---------------------------------------------------------------------------------------
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
/** 1、只針對接口是InstantiationAwareBeanPostProcessor 類型的**/
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
/* 2、bean 初始化前置處理。如果返回null;後面的所有
* 後置處理器的方法就不執行,直接返回(所以執行順序很重要)
*/
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
總結:真正創建bean之前會給處理器一個機會看是否要給目標bean生成動態代理對象,這兒的機會主要是針對AOP的,普通的Bean創建都會返回null,然後開始按正常創建Bean的邏輯去執行doCreateBean()方法。
2、InstantiationAwareBeanPostProcessor後置代碼分析:
實例化後置處理這部分代碼主要在populateBean()方法中,這在圖<4-1>中有體現
AbstractAutowireCapableBeanFactory::
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
/*
* 1、又是給一個機會
* 在屬性被填充前,給 InstantiationAwareBeanPostProcessor 類型的後置處理器一個修改
* bean 狀態的機會。關於這段後置引用,官方的解釋是:讓用戶可以自定義屬性注入。比如用戶實現一
* 個 InstantiationAwareBeanPostProcessor 類型的後置處理器,並通過
* postProcessAfterInstantiation 方法向 bean 的成員變量注入自定義的信息。當然,如果無
* 特殊需求,直接使用配置中的信息注入即可。另外,Spring 並不建議大家直接實現
* InstantiationAwareBeanPostProcessor 接口,如果想實現這種類型的後置處理器,更建議
* 通過繼承 InstantiationAwareBeanPostProcessorAdapter 抽象類實現自定義後置處理器。
*/
/** Give any InstantiationAwareBeanPostProcessors the opportunity to modify
** the state of the bean before properties are set. This can be used, for
** example, to support styles of field injection.
**/
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation) {
return;
}
/... 省略....
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
總結:這個postProcessAfterInstantiation返回值要注意,因爲它的返回值是決定要不要調用postProcessPropertyValues方法的其中一個因素(因爲還有一個因素是mbd.getDependencyCheck());如果該方法返回false,並且不需要check,那麼postProcessPropertyValues就會被忽略不執行;如果返true,postProcessPropertyValues就會被執行。
3、BeanPostProcessor的前置、後置分析源碼:
在圖<4-1>中有體現,處理器插入點其實是在init-method方法生效前後調用postProcessBeforeInitialization和postProcessAfterInitialization方法
AbstractAutowireCapableBeanFactory::
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// ...省略...
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
/** 1、執行 bean 初始化前置操作**/
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
/*
* 2、調用初始化方法:
* ①. 若 bean 實現了 InitializingBean 接口,則調用 afterPropertiesSet 方法
* ②. 若用戶配置了 bean 的 init-method 屬性,則調用用戶在配置中指定的方法
*/
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
/**3、 執行 bean 初始化後置操作,注意AOP 就是在後置處理 postProcessAfterInitialization 方法
** 中向目標對象中織如切面邏輯的, AOP 模塊中的AbstractAutoProxyCreator抽象類間接實現了這個接口中
* 的postProcessBeforeInstantiation方法,AOP代理的創建就是在這個方法內部循環processors,通過
* AbstractAutoProxyCreator創建
*
**/
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
總結:通過上面源碼分析在加上面流程圖解應該是很清晰了:
1、實例化完成,屬性賦值完成
2、執行初始化前置處理邏輯一般bean的創建會返回原bean
3、執行實現了接口的初始化方法
4、執行初始化後置處理邏輯一般bean的創建會返回原bean
五、BeanFactory和ApplicationContext前世今生:
Spring的IoC容器就是一個實現了BeanFactory接口的可實例化類。事實上,Spring提供了兩種不同的容器:一種是最基本的BeanFactory,另一種是擴展的ApplicationContext。BeanFactory 僅提供了最基本的依賴注入支持,而 ApplicationContext 則擴展了BeanFactory ,提供了更多的額外功能。二者對Bean的初始化也有很大區別。BeanFactory當需要調用時讀取配置信息,生成某個類的實例。如果讀入的Bean配置正確,則其他的配置中有錯誤也不會影響程序的運行。而ApplicationContext 在初始化時就把 xml 的配置信息讀入內存,對 XML 文件進行檢驗,如果配置文件沒有錯誤,就創建所有的Bean ,直接爲應用程序服務。相對於基本的BeanFactory,ApplicationContext 唯一的不足是佔用內存空間。當應用程序配置Bean較多時,程序啓動較慢。
ApplicationContext會利用Java反射機制自動識別出配置文件中定義的BeanPostProcessor、InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,並自動將它們註冊到應用上下文中;而BeanFactory需要在代碼中通過手工調用addBeanPostProcessor()方法進行註冊。
類圖如下ApplicationContext是繼承了BeanFactory,並且也繼承了其它的接口,說明ApplicationContext是對BeanFactory接口的擴展,具備BeanFactory不具有的功能。
1、BeanFactory介紹:
Spring容器最基本的接口就是BeanFactory。BeanFactory負責配置、創建、管理Bean,它有一個子接口ApplicationContext也被稱爲Spring上下文,容器同時還管理着Bean和Bean之間的依賴關係。spring Ioc容器的實現,從根源上是beanfactory,但真正可以作爲一個可以獨立使用的ioc容器還是DefaultListableBeanFactory,因此可以這麼說,
DefaultListableBeanFactory 是整個spring ioc的始祖。BeanFactory是Spring bean容器的根接口,提供獲取bean,是否包含bean,是否單例與原型,獲取bean類型,bean 別名的方法 。它最主要的方法就是getBean(String beanName),因此前面創建Bean所介紹的主要類都實現自BeanFactory接口。
BeanFactory的三個子接口:
* HierarchicalBeanFactory:提供父容器的訪問功能
* ListableBeanFactory:提供了批量獲取Bean的方法
* AutowireCapableBeanFactory:它繼承了BeanFactory接口,該接口是一個自動注入的ioc bean工廠接口,主要定義bean的創建、初始化、自動注入以及bean初始化前置、後置增強的接口;在BeanFactory基礎上實現對已存在實例的管理。2、ApplicationContext介紹:
ApplicationContext常用實現類 作用 AnnotationConfigApplicationContext 從一個或多個基於java的配置類中加載上下文定義,適用於java註解的方式。 ClassPathXmlApplicationContext 從類路徑下的一個或多個xml配置文件中加載上下文定義,適用於xml配置的方式。 FileSystemXmlApplicationContext 從文件系統下的一個或多個xml配置文件中加載上下文定義,也就是說系統盤符中加載xml配置文件。 AnnotationConfigWebApplicationContext 專門爲web應用準備的,適用於註解方式。 XmlWebApplicationContext 從web應用下的一個或多個xml配置文件加載上下文定義,適用於xml配置方式。 由於ApplicationContext會預先初始化所有的Singleton Bean,於是在系統創建前期會有較大的系統開銷,但一旦ApplicationContext初始化完成,程序後面獲取Singleton Bean實例時候將有較好的性能。也可以爲bean設置lazy-init屬性爲true,即Spring容器將不會預先初始化該bean。
六、無所不知的Aware:
spring Aware 的目的是爲了讓bean獲取spring容器的服務,Aware接口如上圖所示。
我們只介紹常用的這三個接口:
- BeanNameAware :可以獲取容器中bean的名稱
- BeanFactoryAware:獲取當前bean factory這也可以調用容器的服務
- ApplicationContextAware: 當前的applicationContext, 這也可以調用容器的服務
還記得概述中 圖<1>Bean的實例化流程圖嗎,屬性賦值的下一步就是Aware接口檢查,我們來分析下源碼:
在屬性賦值之後,bean初始化之前會有一個檢查設置Aware的機會,以便爲bean提供Spring的一些資源。在invokeAwareMethods()方法中只檢測了上面三個Aware接口,那麼問題來了其他的Aware接口怎麼設值,例如開發中經常自定義實現了ApplicationContextAware用來獲取應用上下文,它的setApplicationContext()方法是在哪裏被調用的呢?
答:如果bean實現了ApplicationContextAware接口,它的setApplicationContext()方法將被調用,將應用上下文的引用傳入到bean中-------此處的調用設置其實是在處理器ApplicationContextAwareProcessor中做的,ApplicationContextAwareProcessor實現了BeanPostProcessor接口。
AbstractAutowireCapableBeanFactory::
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
/** 1、檢查Aware接口並進行設置,重點介紹此方法 **/
invokeAwareMethods(beanName, bean);
}
/** 2、很熟悉了,就是初始化前後增強,前面已經介紹不再多說**/
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
=============================================================================================
/**
如果bean實現了BeanNameAware接口,spring將bean的id傳給setBeanName()方法;
如果bean實現了BeanFactoryAware接口,spring將調用setBeanFactory方法,將BeanFactory實例傳進來;
**/
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
七、Context的初始化過程:
Context的初始化過程在概述中<6、Context的初始化過程>已做簡單敘述,大致過程如概述中的第6小節的圖<2>所示,具體每一步我們來看源碼:
AnnotationConfigApplicationContext:: public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. //準備上下文的刷新, prepareRefresh(); // Tell the subclass to refresh the internal bean factory. //得到新的Bean工廠,應用上下文加載bean就是在這裏面實現的 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //準備bean工廠用在上下文中 // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. //允許子類上下問處理bean工廠 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. //請求工廠處理器作爲beans註冊在上下文 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. //註冊bean處理器攔截bean創建 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. //初始化上下文中消息源 initMessageSource(); // Initialize event multicaster for this context. //初始化上下文中事件廣播 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. //初始化其他具體bean onRefresh(); // Check for listener beans and register them. //檢查監聽bean並註冊 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. //實例化未初始化單例 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. //最後一步發佈相應事件 finishRefresh(); }
AnnotationConfigApplicationContext::
-------> AbstractApplicationContext: refresh() bean加載的入口
-------> obtainFreshBeanFactory():獲取BeanFactory(返回GenericApplicationContext創建的BeanFactory對象--DefaultListableBeanFactory)
--------> prepareBeanFactory(beanFactory);方爲了下面在Spring上下文中使用BeanFactory做準備(對剛纔創建BeanFactory對象進行一些設置屬性)
--------> postProcessBeanFactory(beanFactory);BeanFactory準備工作完成後進行的後置處理工作;子類通過重寫這個方法來在BeanFactory創建並預準備完成以後做進一步的設置
--------> invokeBeanFactoryPostProcessors(beanFactory);該方法的調用必須在所有的singleton實例化之前,而且從該方法的名稱可以看出來,引入了IOC的Processor角色(其實就是Bean工廠裏的處理器)。protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { /** 就是將所有的BeanFactoryPostProcessor調用了一遍**/ PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
BeanFactoryPostProcessor:BeanFactory的後置處理器。在BeanFactory標準初始化之後執行,執行BeanFactoryPostProcessor的方法
兩個接口:BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
1)、執行BeanFactoryPostProcessor的方法;
先執行BeanDefinitionRegistryPostProcessor
1)、獲取所有的BeanDefinitionRegistryPostProcessor;
2)、看先執行實現了PriorityOrdered優先級接口的BeanDefinitionRegistryPostProcessor、
postProcessor.postProcessBeanDefinitionRegistry(registry)
3)、再執行實現了Ordered順序接口的BeanDefinitionRegistryPostProcessor;
postProcessor.postProcessBeanDefinitionRegistry(registry)
4)、最後執行沒有實現任何優先級或者是順序接口的BeanDefinitionRegistryPostProcessors;
postProcessor.postProcessBeanDefinitionRegistry(registry)
再執行BeanFactoryPostProcessor的方法
1)、獲取所有的BeanFactoryPostProcessor
2)、看先執行實現了PriorityOrdered優先級接口的BeanFactoryPostProcessor、
postProcessor.postProcessBeanFactory()
3)、在執行實現了Ordered順序接口的BeanFactoryPostProcessor;
postProcessor.postProcessBeanFactory()
4)、最後執行沒有實現任何優先級或者是順序接口的BeanFactoryPostProcessor;
postProcessor.postProcessBeanFactory()
--------> registerBeanPostProcessors(beanFactory): 註冊BeanPostProcessor(Bean的後置處理器);不同接口類型的BeanPostProcessor,在Bean創建前後的執行時機是不一樣的。【 intercept bean creation】
--------> finishBeanInitialization(beanFactory): 這個方法裏面,用來初始化非懶加載的bean。並非所有bean都在容器啓動的時候實例化。在xml中配置bean的時候,有個lazy-ini屬性,默認爲false。所以默認情況下,單例的非懶加載的bean在容器啓動的時候會實例化。如果是懶加載的,那麼在getBean的時候,再實例化。(前面已經具體分析過)總結:
1)、Spring容器在啓動的時候,先會保存所有註冊進來的Bean的定義信息;
1)、xml註冊bean;<bean>
2)、註解註冊Bean;@Service、@Component、@Bean、xxx
2)、Spring容器會合適的時機創建這些Bean
1)、用到這個bean的時候;利用getBean創建bean;創建好以後保存在容器中;
2)、統一創建剩下所有的bean的時候;finishBeanFactoryInitialization();
3)、後置處理器;BeanPostProcessor
1)、每一個bean創建完成,都會使用各種後置處理器進行處理;來增強bean的功能;
AutowiredAnnotationBeanPostProcessor:處理自動注入
AnnotationAwareAspectJAutoProxyCreator:來做AOP功能;
xxx....
增強的功能註解:
AsyncAnnotationBeanPostProcessor
....