Spring的3級緩存和循環(huán)引用的理解

此處是我自己的一個理解肠缔,防止以后忘記鸯两,如若那個地方理解不對,歡迎指出蓉冈。

一城舞、背景

在我們寫代碼的過程中一般會使用 @Autowired 來注入另外的一個對象,但有些時候發(fā)生了循環(huán)依賴寞酿,但是我們的代碼沒有報錯家夺,這個是什么原因呢?

二伐弹、前置知識

1拉馋、考慮循環(huán)依賴的類型

此處我們考慮 單例 + @Autowired 的循環(huán)依賴,不考慮使用構造器注入原型作用域的Bean的注入惨好。

2煌茴、代理對象何時創(chuàng)建

代理對象何時創(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)生一個新的對象或返回舊對象僚害,取決于是否存在代理等等硫椰。

ObjectFactory#getObject()

4、從3級緩存中獲取對象

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)

從3級緩存中獲取對象

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 的簡化流程可知

image.png

image.png

由上圖可知随珠,對象存在代理時,2級緩存無法解決問題杆烁。因為代理對象是通過BeanPostProcessor來完成牙丽,是在設置屬性之后才產(chǎn)生的代理對象

此時可能有人會說兔魂,那如果我在構建完B的實例后,就立馬進行Aop代理举娩,這樣不就解決問題了嗎析校?那假設A和B之間沒有發(fā)生循環(huán)依賴构罗,這樣設計會不會不優(yōu)雅?

2智玻、假設只有singletonObjects和singletonFactories可否完成循環(huán)依賴

image.png

由圖中可知也是不可以實現(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級緩存多次獲取的值不一致

image.png

從上圖中可知隧熙,對象是先從 一級->二級->三級緩存 這樣查找,當三級緩存產(chǎn)生了對象后就放入二級緩存中緩存起來幻林,同時刪除三級緩存贞盯。

3、流程圖

image.png

四沪饺、總結

1躏敢、一級緩存 singletonObjects 存放可以使用的單例。
2整葡、二級緩存earlySingletonObjects存放的是早期的Bean父丰,即是半成品,此時還是不可用的掘宪。
3蛾扇、三級緩存singletonFactories 是一個對象工廠,用于創(chuàng)建對象魏滚,然后放入到二級緩存中镀首。同時對象如果有Aop代理的話,這個對對象工廠返回的就是代理對象鼠次。

那可以在earlySingletonObjects中直接存放創(chuàng)建后的代理對象嗎更哄?這樣是可以解決問題,但是設計可能就不合理了腥寇。因為在Spring中 Aop的代理是在對象完成之后創(chuàng)建的成翩。而且如果沒有發(fā)生循環(huán)依賴的話,有必要提前創(chuàng)建代理對象嗎赦役?分成三級緩存麻敌,代碼結構更清楚,更合理掂摔。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末术羔,一起剝皮案震驚了整個濱河市赢赊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌级历,老刑警劉巖释移,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異寥殖,居然都是意外死亡玩讳,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門嚼贡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來熏纯,“玉大人,你說我怎么就攤上這事编曼《咕蓿” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵掐场,是天一觀的道長往扔。 經(jīng)常有香客問我,道長熊户,這世上最難降的妖魔是什么萍膛? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮嚷堡,結果婚禮上蝗罗,老公的妹妹穿的比我還像新娘。我一直安慰自己蝌戒,他們只是感情好串塑,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著北苟,像睡著了一般桩匪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上友鼻,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天傻昙,我揣著相機與錄音,去河邊找鬼彩扔。 笑死妆档,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的虫碉。 我是一名探鬼主播贾惦,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纤虽?” 一聲冷哼從身側響起乳绕,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绞惦,失蹤者是張志新(化名)和其女友劉穎逼纸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體济蝉,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡杰刽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了王滤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贺嫂。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖雁乡,靈堂內(nèi)的尸體忽然破棺而出第喳,到底是詐尸還是另有隱情,我是刑警寧澤踱稍,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布曲饱,位于F島的核電站,受9級特大地震影響珠月,放射性物質(zhì)發(fā)生泄漏扩淀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一啤挎、第九天 我趴在偏房一處隱蔽的房頂上張望驻谆。 院中可真熱鬧,春花似錦庆聘、人聲如沸胜臊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽象对。三九已至,卻和暖如春澳腹,著一層夾襖步出監(jiān)牢的瞬間织盼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工酱塔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沥邻,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓羊娃,卻偏偏與公主長得像唐全,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容