Spring的3級快取和迴圈引用的理解
此處是我自己的一個理解,防止以後忘記,如若那個地方理解不對,歡迎指出。
一、背景
在我們寫程式碼的過程中一般會使用 @Autowired
來注入另外的一個物件,但有些時候發生了 迴圈依賴
,但是我們的程式碼沒有報錯,這個是什麼原因呢?
二、前置知識
1、考慮迴圈依賴的型別
此處我們考慮 單例
+ @Autowired
的迴圈依賴,不考慮使用構造器注入
或原型作用域的Bean
的注入。
2、代理物件何時建立
注意:
正常情況下,即沒有發生 迴圈依賴的時候,aop增強
是在 bean 初始化完成之後的 BeanPostProcessor#postProcessAfterInitialization
方法中,但是如果有迴圈依賴發生的話,就需要提前,在 getEarlyBeanReference
3、3級快取中儲存的是什麼物件
快取欄位名 | 快取級別 | 資料型別 | 解釋 |
---|---|---|---|
singletonObjects | 1 | Map<String, Object> | 儲存的是完整的Bean,即可以使用的Bean |
earlySingletonObjects | 2 | Map<String, Object> | 儲存的是半成品的Bean,即屬性還沒有設定,沒有完成初始化工作 |
singletonFactories | 3 | Map<String, ObjectFactory<?>> | 主要是生成Bean,然後放到二級快取中 |
注意:ObjectFactory#getObject()
4、從3級快取中獲取物件
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
5 Spring Bean的簡化建立過程
1、例項化一個bean
Object bean = instanceWrapper.getWrappedInstance();
例項化Bean 即 new Bean()
2、加入到三級快取中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
加入到三級快取中是有一些條件判斷的,一般都會是成立的,此處認為需要加入到三級快取。
3、設定bean的屬性
populateBean(beanName, mbd, instanceWrapper);
第一步例項化了bean,但是此時是沒有填充需要注入的屬性的,通過這一步進行屬性的填充。
4、初始化bean
Object exposedObject = initializeBean(beanName, exposedObject, mbd);
初始化Bean,執行初始化方法、Aware回撥、執行 BeanPostProcessor#postProcessAfterInitialization 方法 (aop的增強
是在這個裡面實現的)
如果有迴圈引用
的話,則aop的增強需要提前。
5、加入到一級快取中
addSingleton(......)
三、理解
@Component
class A {
@Autowired
private B b;
}
@Transaction (存在代理)
@Component
class B{
@Autowired
private A a;
}
1、假設只有singletonObjects和earlySingletonObjects可否完成迴圈依賴
快取欄位名 | 快取級別 | 資料型別 | 解釋 |
---|---|---|---|
singletonObjects | 1 | Map<String, Object> | 儲存的是完整的Bean,即可以使用的Bean |
earlySingletonObjects | 2 | Map<String, Object> | 儲存的是半成品的Bean,即屬性還沒有設定,沒有完成初始化工作 |
此時需要獲取 B
的例項,即 getBean("b")
,由上方瞭解到的 Bean 的簡化流程可知
由上圖可知,物件存在代理時
,2級快取無法解決問題。因為代理物件是通過BeanPostProcessor
來完成,是在設定屬性之後才產生的代理物件
。
此時可能有人會說,那如果我在構建完B的例項後,就立馬進行Aop代理,這樣不就解決問題了嗎?那假設A和B之間沒有發生迴圈依賴,這樣設計會不會不優雅?
2、假設只有singletonObjects和singletonFactories可否完成迴圈依賴
由圖中可知也是不可以實現的。
3、3級快取如何實現
1、解決代理問題
因為預設情況下,代理是通過BeanPostProcessor
來完成,為了解決代理,就需要提前建立代理,那麼這個代理的建立就放到3級快取中來進行建立。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
getEarlyBeanReference
此方法會返回代理bean
2、解決單例通過第3級快取多次獲取的值不一致
從上圖中可知,物件是先從 一級->二級->三級快取
這樣查詢,當三級快取產生了物件後就放入二級快取中快取起來,同時刪除三級快取。
3、流程圖
四、總結
1、一級快取 singletonObjects
存放可以使用的單例。
2、二級快取earlySingletonObjects
存放的是早期的Bean,即是半成品,此時還是不可用的。
3、三級快取singletonFactories
是一個物件工廠,用於建立物件,然後放入到二級快取中。同時物件如果有Aop代理的話,這個對物件工廠返回的就是代理物件。
那可以在earlySingletonObjects
中直接存放建立後的代理物件嗎?這樣是可以解決問題,但是設計可能就不合理了。因為在Spring中 Aop
的代理是在物件完成之後建立的。而且如果沒有發生迴圈依賴的話,有必要提前建立代理物件嗎?分成三級快取,程式碼結構更清楚,更合理。