一、前言 承接 "《Spring源碼解析——創建bean》" 、 "《Spring源碼解析——創建bean的實例》" ,我們今天接著聊聊,迴圈依賴的解決方案,即創建bean的ObjectFactory。 二、ObjectFactory 這段代碼不是很複雜,但是很多人不是太理解這段代碼的作用,而且,這 ...
一、前言
承接《Spring源碼解析——創建bean》、《Spring源碼解析——創建bean的實例》,我們今天接著聊聊,迴圈依賴的解決方案,即創建bean的ObjectFactory。
二、ObjectFactory
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 為避免後期迴圈依賴,可以在bean初始化完成前將創建實例的ObjectFactory加入工廠
/**
* getEarlyBeanReference(beanName, mbd, bean)方法:
* 對bean再一次依賴引用,主要應用SmartInstantiationAwareBeanPostProcessor
* 其中我們熟知的AOP就是在這裡將advice動態織入bean中,若沒有則直接返回bean,不做任何處理
*/
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
這段代碼不是很複雜,但是很多人不是太理解這段代碼的作用,而且,這段代碼僅從此函數中去理解也很難弄懂其中的含義,我們需要從全局的角度去思考 Spring 的依賴解決辦法。
earlySingletonExposure :從字面的意思理解就是提早曝光的單例,我們暫不定義它的學名叫什麼,我們感興趣的是有哪些條件影響這個值。
- mbd.isSingleton() :沒有太多可以解釋的,此 RootBeanDefinition 代表的是否是單例。
- this.allowCircularReferences :是否允許迴圈依賴,很抱歉,並沒有找到在配置文件中如何配置,但是在 AbstractRefreshableApplicationContext 中提供了設置函數,可以通過硬編碼的方式進行設置或者可以通過自定義命名空間進行配置,其中硬編碼的方式代碼如下。
ClassPathXmlApplicationContext bf = ClassPathXmlApplicationContext("aspectTest.xml" ); bt.setAllowBeanDefinitionOverriding(false);
- isSingletonCurrentlylncreation(beanName) :該 bean 是否在創建中。在 Spring 中,會有個專門的屬性預設為 DefaultSingletonBeanRegistry的 singletonsCurrentlylnCreation 來記錄 bean 的載入狀態,在 bean 開始創建前會將 beanName 記錄在屬性中,在 bean 創建結束後會將 beanName 從屬性中移除。那麼我們跟隨代碼一路走來可是對這個屬性的記錄並沒有多少印象,這個狀態是在哪裡記錄的呢?不同 scope 的記錄位置並不一樣,我們以 singleton 為例,在 singleton 下記錄屬性的函數是在 DefaultSingletonBeanRegistry的 public Object getSingleton(String beanName, ObjectFactory singletonFactory)函數的 beforeSingletonCreation(beanName)和 afterSingletonCreation(beanName)中,在這兩段函數中分別this.singletonCurrentlylnCreation.add(beanName)與 this.singletonCurrentlylnCreation.remove(beanName)來進行狀態的記錄與移除。
經過以上分析我們瞭解變數 earl earlySingletonExposure 是否是單例、是否允許迴圈依賴、是否對應的 bean 正在創建的條件的綜合。當這 3 個條件都滿足時會執行 addSingletonFactory操作,那麼加入 SingletonFactory的作用是什麼呢?又是在什麼時候調用呢?
我們還是以最簡單的AB迴圈依賴為例,類A中含有屬性類B,而類B中又會含有屬性類A,那麼初始化beanA的過程如下圖所示:
上圖展示了創建 beanA 的流程,圖中我們看到,在創建 A 的時候首先會記錄類 A 所對應的 beanName,並將beanA的創建工廠加入緩存中,而在對 A的屬性填充也就是調用populate方法的時候又會再一次的對 B 進行遞歸創建。同樣的,因為在 B 中同樣存在 A 屬性,因此在實例化 B 的的 populate 方法中又會再次地初始化 A ,也就是圖形的最後,調用 getBean(A)。關鍵是在這裡,有心的同學可以去找找這個代碼的實現方式,我們之前已經講過,在這個函數中並不是直接去實例化 A ,而是先去檢測緩存中是否有已經創建好的對應的 bean ,或者是否已經創建好的 ObjectFactory,而此時對於A的 ObjectFactory我們早已經創建,所以便不會再去向後執行,而是直接調用 ObjectFactory去創建 A 。這裡最關鍵的是 ObjectFactory的實現。
/**
* getEarlyBeanReference(beanName, mbd, bean)方法:
* 對bean再一次依賴引用,主要應用SmartInstantiationAwareBeanPostProcessor
* 其中我們熟知的AOP就是在這裡將advice動態織入bean中,若沒有則直接返回bean,不做任何處理
*/
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
其中getEarlyBeanReference的代碼如下:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
在 getEarlyBeanReference 函數中並沒有太多的邏輯處理,或者說除了後處理器的調用外沒有別的處理工作,根據以上分析,基本可以理清 spring 處理迴圈依賴的解決辦法,在 B 中創建依賴 A 時通過 ObjectFactory 提供的實例化方法來中斷 A 中的屬性填充,使 B 中持有的 A 僅僅是剛剛初始化並沒有填充任何屬性的 A ,而這正初始化 A 的步驟還是在最開始創建 A 的時候進行的,但是因為 A 與 B 中的 A 所表示的屬性地址是一樣的,所以在 A 中創建好的屬性填充自然可以通過 B 中的 A 獲取,這樣就解決了迴圈依賴的問題。
三、小結
大體上的原理就是這樣,有什麼不明白的地方,大伙可繼續閱讀如下文章:
https://blog.csdn.net/m0_38043362/article/details/80284577
https://blog.csdn.net/hzcao/article/details/78479593
http://book.51cto.com/art/201311/419098.htm
本文在米兜公眾號鏈接
https://mp.weixin.qq.com/s/P7f0HLnyjHqoN4-rUm0ytQ
歡迎關註米兜Java,一個註在共用、交流的Java學習平臺。