此處是我自己的一個理解肠缔,防止以后忘記鸯两,如若那個地方理解不對,歡迎指出蓉冈。
一城舞、背景
在我們寫代碼的過程中一般會使用 @Autowired
來注入另外的一個對象,但有些時候發(fā)生了循環(huán)依賴
寞酿,但是我們的代碼沒有報錯家夺,這個是什么原因呢?
二伐弹、前置知識
1拉馋、考慮循環(huán)依賴的類型
此處我們考慮 單例
+ @Autowired
的循環(huán)依賴,不考慮使用構造器注入
或原型作用域的Bean
的注入惨好。
2煌茴、代理對象何時創(chuàng)建
注意:
正常情況下,即沒有發(fā)生 循環(huán)依賴的時候日川,aop增強
是在 bean 初始化完成之后的 BeanPostProcessor#postProcessAfterInitialization
方法中蔓腐,但是如果有循環(huán)依賴發(fā)生的話,就需要提前龄句,在 getEarlyBeanReference
中提前創(chuàng)建代理對象回论。
3散罕、3級緩存中保存的是什么對象
緩存字段名 | 緩存級別 | 數(shù)據(jù)類型 | 解釋 |
---|---|---|---|
singletonObjects | 1 | Map<String, Object> | 保存的是完整的Bean,即可以使用的Bean |
earlySingletonObjects | 2 | Map<String, Object> | 保存的是半成品的Bean,即屬性還沒有設置傀蓉,沒有完成初始化工作 |
singletonFactories | 3 | Map<String, ObjectFactory<?>> | 主要是生成Bean笨使,然后放到二級緩存中 |
注意:
ObjectFactory#getObject()
每調(diào)用一次,都會產(chǎn)生一個新的對象或返回舊對象僚害,取決于是否存在代理等等硫椰。
4、從3級緩存中獲取對象
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
5 Spring Bean的簡化創(chuàng)建過程
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驾窟,執(zhí)行初始化方法、Aware回調(diào)认轨、執(zhí)行 BeanPostProcessor#postProcessAfterInitialization 方法 (aop的增強
是在這個里面實現(xiàn)的)
如果有循環(huán)引用
的話绅络,則aop的增強需要提前。
5嘁字、加入到一級緩存中
addSingleton(......)
三恩急、理解
@Component
class A {
@Autowired
private B b;
}
@Transaction (存在代理)
@Component
class B{
@Autowired
private A a;
}
1、假設只有singletonObjects和earlySingletonObjects可否完成循環(huán)依賴
緩存字段名 | 緩存級別 | 數(shù)據(jù)類型 | 解釋 |
---|---|---|---|
singletonObjects | 1 | Map<String, Object> | 保存的是完整的Bean纪蜒,即可以使用的Bean |
earlySingletonObjects | 2 | Map<String, Object> | 保存的是半成品的Bean,即屬性還沒有設置衷恭,沒有完成初始化工作 |
此時需要獲取 B
的實例,即 getBean("b")
纯续,由上方了解到的 Bean 的簡化流程可知
由上圖可知随珠,對象存在代理時
,2級緩存無法解決問題杆烁。因為代理對象是通過BeanPostProcessor
來完成牙丽,是在設置屬性之后才產(chǎn)生的代理對象
。
此時可能有人會說兔魂,那如果我在構建完B的實例后,就立馬進行Aop代理举娩,這樣不就解決問題了嗎析校?那假設A和B之間沒有發(fā)生循環(huán)依賴构罗,這樣設計會不會不優(yōu)雅?
2智玻、假設只有singletonObjects和singletonFactories可否完成循環(huán)依賴
由圖中可知也是不可以實現(xiàn)的遂唧。
3、3級緩存如何實現(xiàn)
1吊奢、解決代理問題
因為默認情況下盖彭,代理是通過BeanPostProcessor
來完成,為了解決代理页滚,就需要提前創(chuàng)建代理召边,那么這個代理的創(chuàng)建就放到3級緩存中來進行創(chuàng)建。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
getEarlyBeanReference
此方法會返回代理bean
2裹驰、解決單例通過第3級緩存多次獲取的值不一致
從上圖中可知隧熙,對象是先從 一級->二級->三級緩存
這樣查找,當三級緩存產(chǎn)生了對象后就放入二級緩存中緩存起來幻林,同時刪除三級緩存贞盯。
3、流程圖
四沪饺、總結
1躏敢、一級緩存 singletonObjects
存放可以使用的單例。
2整葡、二級緩存earlySingletonObjects
存放的是早期的Bean父丰,即是半成品,此時還是不可用的掘宪。
3蛾扇、三級緩存singletonFactories
是一個對象工廠,用于創(chuàng)建對象魏滚,然后放入到二級緩存中镀首。同時對象如果有Aop代理的話,這個對對象工廠返回的就是代理對象鼠次。
那可以在earlySingletonObjects
中直接存放創(chuàng)建后的代理對象嗎更哄?這樣是可以解決問題,但是設計可能就不合理了腥寇。因為在Spring中 Aop
的代理是在對象完成之后創(chuàng)建的成翩。而且如果沒有發(fā)生循環(huán)依賴的話,有必要提前創(chuàng)建代理對象嗎赦役?分成三級緩存麻敌,代碼結構更清楚,更合理掂摔。