Spring 源碼分析之 Spring 三級緩存解決循環(huán)依賴問題

1究珊,什么是 Spring 的循環(huán)依賴

簡單來講薪者,就是有一個 A 對象,創(chuàng)建 A 的時候發(fā)現(xiàn) A 對象依賴 B剿涮,然后去創(chuàng)建 B 對象的時候言津,又發(fā)現(xiàn) B 對象依賴 C,然后去創(chuàng)建 C 對象的時候取试,又發(fā)現(xiàn) C 對象依賴 A悬槽。這就是所謂的循環(huán)依賴。

那么 Spring 在創(chuàng)建 Bean 的時候是如何解決這種關(guān)系的依賴呢瞬浓?(循環(huán)依賴)先拋出結(jié)論猿棉,Spring 使用了三級緩存解決了循環(huán)依賴的問題。并且 Spring 能解決哪些循環(huán)依賴不能解決哪些循環(huán)依賴宪躯。以下會詳細(xì)的闡述位迂。

2,什么是三級緩存

1臣缀,第一級緩存:單例緩存池 singletonObjects。

2计寇,第二級緩存:早期提前暴露的對象緩存 earlySingletonObjects脂倦。

3赖阻,第三級緩存:singletonFactories 單例對象工廠緩存


3,什么是早期暴露的對象

所謂的早提提前暴露的對象就是說棋电,你是一個不完整的對象苇侵,你的屬性還沒有值榆浓,你的對象也沒有被初始化。這就是早期暴露的對象烘浦,只是提前拿出來給你認(rèn)識認(rèn)識杉适。但他非常重要猿推。這是多級緩存解決循環(huán)依賴問題的一個巧妙的地方。


4藕咏,創(chuàng)建 Bean 的整個過程

getSingleton 方法詳解

1秽五,getBean 方法肯定不陌生,必經(jīng)之路盲再,然后調(diào)用 doGetBean,進(jìn)來以后首先會執(zhí)行 transformedBeanName 找別名贷揽,看你的 Bean 上面是否起了別名禽绪。然后進(jìn)行很重要的一步洪规,getSingleton,這段代碼就是從你的單例緩存池中獲取 Bean 的實例雄人。那么你第一次進(jìn)來肯定是沒有的柠衍,緩存里肯定是拿不到的晶乔。也就是一級緩存里是沒有的牺勾。那么它怎么辦呢?他會嘗試去二級緩存中去拿翻具,但是去二級緩存中拿并不是無條件的裆泳,首先要判斷 isSingletonCurrentlyInCreation(beanName) 他要看你這個對象是否正在創(chuàng)建當(dāng)中柠硕,如果不是直接就退出該方法,如果是的話闻葵,他就會去二級緩存 earlySingletonObjects 里面取槽畔,如果沒拿到胁编,它還接著判斷 allowEarlyReference 這個東西是否為 true鳞尔。它的意思是說铅檩,是否允許讓你從單例工廠對象緩存中去拿對象昧旨。默認(rèn)為 true祥得。好了,此時如果進(jìn)來那么就會通過 singletonFactory.getObject()乒疏;去單例工廠緩存中去拿怕吴。然后將緩存級別提升至二級緩存也就早期暴露的緩存县踢。然后,如果說你第一次都沒有從單例緩存中沒有獲取到對象并且 isSingletonCurrentlyInCreation 也沒有被標(biāo)記议经。那么直接就返回了后面的流程都不走煞肾。

dependsOn

2嗓袱,getSingleton 執(zhí)行完以后會走 dependsOn 方法,判斷是否有 dependsOn 標(biāo)記的循環(huán)引用蝙昙,有的話直接卡死耸黑,拋出異常篮幢。比如說 A 依賴于 B,B 依賴于 A 通過 dependsOn 注解去指定缺菌。此時執(zhí)行到這里就會拋出異常。這里所指并非是構(gòu)造函數(shù)的循環(huán)依賴.

beforeSingletonCreation

3耿战,在這里方法里焊傅。就把你的對象標(biāo)記為了早期暴露的對象。提前暴露對象用于創(chuàng)建 Bean 的實例

createBean

4鸭栖,緊接著就走創(chuàng)建 Bean 的流程開始晕鹊。在創(chuàng)建 Bean 之前執(zhí)行了一下 resolveBeforeInstantiation 這個東東。它的意思是說溅话,代理 AOPBean 定義注冊信息(也就是那個蛋)但是這里并不是實際去代理你的對象飞几,因為你只是個蛋同规。你還沒有被創(chuàng)建券勺,我給你代理個錘子啊关炼?只是代理了你的 Bean 定義信息匣吊,還沒有被實例化。把你的 Bean 定義信息放進(jìn)緩存色鸳,以便我想代理真正的目標(biāo)對象的時候,直接去緩存里去拿蒜哀。

doCreateBean

5吏砂,接下來就真正的走你的創(chuàng)建 Bean 流程

首先走進(jìn)真正做事兒的方法 doCreateBean 然后找到 createBeanInstance 這個方法,在這里面它將為你創(chuàng)建你的 Bean 實例信息(Bean 的實例)淀歇。如果說創(chuàng)建成功了浪默,那么就把你的對象放入緩存中去(將創(chuàng)建好的提前曝光的對象放入 singletonFactories 三級緩存中)將對象從二級緩存中移除因為它已經(jīng)不是提前暴露的對象了。但是纳决。如果說在 createBeanInstance 這個方法中在創(chuàng)建 Bean 的時候它會去檢測你的依賴關(guān)系岳链,會去檢測你的構(gòu)造器。然后约急,如果說它在創(chuàng)建 A 對象的時候苗分,發(fā)現(xiàn)了構(gòu)造器里依賴了 B,然后它又會重新走 getBean 的這個流程奴饮,當(dāng)在走到這里的時候戴卜,又發(fā)現(xiàn)依賴了 A 此時就會拋出異常投剥。為什么會拋出異常,因為江锨,走 getBean 的時候他會去從你的單例緩存池中去拿啄育,因為你這里的 Bean 還沒有被創(chuàng)建好拌消。自然不會被放進(jìn)緩存中,所以它是在緩存中拿不到 B 對象的浮毯。反過來也是拿不到 A 對象的。造成了死循環(huán)故此直接拋異常壳鹤。這就是為什么 Spring IOC 不能解決構(gòu)造器循環(huán)依賴的原因饰迹。因為你還沒來的急放入緩存你的對象是不存在的。所以不能創(chuàng)建锹淌。同理 @Bean 標(biāo)注的循環(huán)依賴方法也是不能解決的赠制,跟這個同理。那么多例就更不能解決了烟号。為什么汪拥?因為在走 createBeanInstance 的時候篙耗,會判斷是否是單例的 Bean 定義信息 mbd.isSingleton();如果是才會進(jìn)來脯燃。所以多例的 Bean 壓根就不會走進(jìn)來曲伊,而是走了另一段邏輯追他,這里不做介紹邑狸。至此单雾,構(gòu)造器循環(huán)依賴和 @Bean 的循環(huán)依賴還有多例 Bean 的循環(huán)依賴為什么不能解決已經(jīng)解釋清楚。然后如果說屿储,Bean 創(chuàng)建成功了够掠。那么會走后面的邏輯


6疯潭,將創(chuàng)建好的 Bean 放入緩存

addSingletonFactory竖哩。方法就是將你創(chuàng)建好的 Bean 放入三級緩存中脊僚。并且移除早期暴露的對象辽幌。

populateBean

7,通過 populateBean 給屬性賦值

我們知道埠通,創(chuàng)建好的對象端辱,并不是一個完整的對象,里面的屬性還沒有被賦值舞蔽。所以這個方法就是為創(chuàng)建好的 Bean 為它的屬性賦值渗柿。并且調(diào)用了我們實現(xiàn)的的 XXXAware 接口進(jìn)行回調(diào)初始化朵栖,陨溅。然后調(diào)用我們實現(xiàn)的 Bean 的后置處理器门扇,給我們最后一次機(jī)會去修改 Bean 的屬性。其他的地方我不做過多的介紹霸奕,我只講一個重點就是吉拳,在 populateBean 里面他會解析你的屬性合武,并且賦值,當(dāng)發(fā)現(xiàn)盟庞,A 對象里面依賴了 B什猖,此時又會走 getBean 方法红淡,但這個時候,你去緩存中是可以拿的到的摇零。因為我們在對 createBeanInstance 對象創(chuàng)建完成以后已經(jīng)放入了緩存當(dāng)中驻仅,所以創(chuàng)建 B 的時候發(fā)現(xiàn)依賴 A噪服,直接就從緩存中去拿粘优,此時 B 創(chuàng)建完雹顺,A 也創(chuàng)建完无拗,一共執(zhí)行了 4 次英染。至此 Bean 的創(chuàng)建完成被饿,最后將創(chuàng)建好的 Bean 放入單例緩存池中 addSingleton()四康;至此 Ioc 創(chuàng)建 Bean 的整個生命周期已經(jīng)介紹完畢。以及 Spring 是如何通過三級緩存去解決循環(huán)依賴的問題狭握。下面附加一張流程腦圖闪金。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市论颅,隨后出現(xiàn)的幾起案子哎垦,更是在濱河造成了極大的恐慌,老刑警劉巖恃疯,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漏设,死亡現(xiàn)場離奇詭異,居然都是意外死亡今妄,警方通過查閱死者的電腦和手機(jī)郑口,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門犬性,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鹤耍,你說我怎么就攤上這事。” “怎么了财著?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵伟姐,是天一觀的道長鹿霸。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么睦袖? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任延蟹,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘高帖。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蹈矮,像睡著了一般含滴。 火紅的嫁衣襯著肌膚如雪勺美。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天华蜒,我揣著相機(jī)與錄音贺拣,去河邊找鬼啥辨。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蒿囤。 我是一名探鬼主播底挫,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起妄辩,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤畔塔,失蹤者是張志新(化名)和其女友劉穎寄摆,沒想到半個月后桑阶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡玛歌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年休弃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棉磨,我是刑警寧澤斟赚,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布刃鳄,位于F島的核電站讨盒,受9級特大地震影響遂鹊,放射性物質(zhì)發(fā)生泄漏邻储。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望妙色。 院中可真熱鬧煌珊,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咱揍。三九已至煤裙,卻和暖如春冯事,著一層夾襖步出監(jiān)牢的瞬間薛夜,已是汗流浹背籍茧。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留梯澜,地道東北人寞冯。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像晚伙,于是被迫代替她去往敵國和親吮龄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

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