循環(huán)依賴(lài)就是我依賴(lài)你、你依賴(lài)我,或者A依賴(lài)B、B依賴(lài)C、C依賴(lài)A…組成的錯(cuò)綜復(fù)雜的依賴(lài)關(guān)系。
其實(shí)各種不同的依賴(lài)關(guān)系最終從邏輯上都可以演變?yōu)椋何乙蕾?lài)你、你依賴(lài)我。
循環(huán)依賴(lài)大致可以分為兩種情況:
- 屬性依賴(lài):比如A對(duì)象有一個(gè)屬性B,B對(duì)象有屬性A。
- 構(gòu)造器依賴(lài):A對(duì)象的構(gòu)造器中有參數(shù)B,B對(duì)象的構(gòu)造器有參數(shù)A。
構(gòu)造器依賴(lài)是Spring解決不了的(指的是A、B不存在其他構(gòu)造器,或者Spring需要通過(guò)互相依賴(lài)的構(gòu)造器創(chuàng)建A、B對(duì)象),這種死結(jié)你自己寫(xiě)代碼都沒(méi)辦法解決,Spring當(dāng)然也沒(méi)有辦法。
Spring通過(guò)其特有的三級(jí)緩存機(jī)制能解決屬性依賴(lài)。順便說(shuō)一下,其實(shí)Spring官網(wǎng)或相關(guān)資料中并沒(méi)有提出過(guò)三級(jí)緩存的說(shuō)法,也不太清楚這種說(shuō)法從哪兒來(lái)(或者是我疏漏了沒(méi)有查到),反正很NB、很高深的樣子,其實(shí)如果你認(rèn)真讀源碼,換做一種真實(shí)的說(shuō)法:Spring最終通過(guò)三個(gè)Map的巧妙使用解決循環(huán)依賴(lài)問(wèn)題…也就沒(méi)有那么NB、那么高深、尤其是沒(méi)有那么高大上了。但是不管怎么說(shuō),Spring解決循環(huán)依賴(lài)的框架還是值得我們認(rèn)真學(xué)習(xí)掌握的。因?yàn)镾pring各路大牛的設(shè)計(jì)思想確實(shí)#@¥%……%*(確實(shí)和你的收入會(huì)直接相關(guān)所以你一定有必要花點(diǎn)時(shí)間精力徹底搞清楚)。
三級(jí)緩存框架
先上一張圖,對(duì)三級(jí)緩存有個(gè)直觀印象:
抱歉,畫(huà)的有點(diǎn)low,不過(guò)還是能說(shuō)明問(wèn)題的哈。
Spring IoC容器中當(dāng)然不止是DefaultSingletonBeanRegistry一個(gè)容器,但三級(jí)緩存就是針對(duì)單例Bena來(lái)說(shuō)的,其實(shí)如果只是從Bean容器的角度來(lái)講,Spring IoC容器很大程度上講的也就是單例Bean的容器,原型Bean本來(lái)也是每次使用、每次創(chuàng)建,所以也就不需要緩存。只有單例Bean在Spring IoC容器中是一次創(chuàng)建長(zhǎng)期駐留,駐留的地方就是三級(jí)緩存中的“一級(jí)緩存”。
如上圖,三級(jí)緩存定義在DefaultSingletonBeanRegistry中:
- 三級(jí)緩存:存儲(chǔ)bean name和bean工廠的容器。
- 二級(jí)緩存:存儲(chǔ)bean name和bean實(shí)例,二級(jí)緩存中的bean實(shí)例剛剛完成實(shí)例化、尚未完成屬性賦值,所以是半成品。
- 一級(jí)緩存:存儲(chǔ)最終完成創(chuàng)建的Bean,應(yīng)用getBean或者自動(dòng)裝配的Bean就是從以及緩存獲取的。
Spring解決循環(huán)依賴(lài)的詳細(xì)過(guò)程
其實(shí)認(rèn)真讀源碼,研究完Spring的Bean創(chuàng)建過(guò)程之后,循環(huán)依賴(lài)的原理就一目了然了(雖然很復(fù)雜)。
Spring的Bean創(chuàng)建過(guò)程相對(duì)來(lái)說(shuō)還是比較復(fù)雜的,研究清楚全貌還是要花費(fèi)點(diǎn)時(shí)間的,我們的策略就是一步一步抽絲剝繭以及各個(gè)擊破,今天的主要目標(biāo)就是三級(jí)緩存解決循環(huán)依賴(lài)的過(guò)程,所以對(duì)其他過(guò)程就要忽略。
Spring源碼,Bean的創(chuàng)建過(guò)程:
AbstractBeanFactory#getBean
創(chuàng)建和獲取bean的統(tǒng)一入口,會(huì)調(diào)用doGetBean方法。
AbstractBeanFactory#doGetBean
- 首先調(diào)用DefaultSingletonBeanRegistry#getSingleton(beanName,true)方法,注意這個(gè)getSingleton方法是不會(huì)創(chuàng)建bean的。
- 如果沒(méi)有拿到Bean,就準(zhǔn)備創(chuàng)建Bean
- 首先檢查是否有Dependon,有的話(huà),先創(chuàng)建Dependon
- 如果要?jiǎng)?chuàng)建的是單例bean,則調(diào)用getSingleton(beanName,factory)創(chuàng)建bean,注意這個(gè)才會(huì)創(chuàng)建bean
- 否則,如果是原型bean…和今天的話(huà)題無(wú)關(guān),暫時(shí)忽略
DefaultSingletonBeanRegistry#getSingleton(beanName,boolean)
我們上面說(shuō)過(guò)了,這里不會(huì)創(chuàng)建bean:
- 一級(jí)緩存存在,則直接返回
- 一級(jí)緩存不存在并且Bean正在創(chuàng)建中,從二級(jí)緩存獲取
- 二級(jí)緩存獲取到則返回,否則檢查三級(jí)緩存
- 三級(jí)緩存有的話(huà),調(diào)用三級(jí)緩存的bean工廠加工bean,放入二級(jí)緩存后,返回,并清除三級(jí)緩存
- 否則三級(jí)緩存沒(méi)有的話(huà)返回null
DefaultSingletonBeanRegistry#getSingleton(beanName,F(xiàn)actory)
這個(gè)getSingleton擔(dān)負(fù)著創(chuàng)建bean實(shí)例的任務(wù),如果獲取不到的話(huà),會(huì)創(chuàng)建:
- 一級(jí)緩存存在,則直接返回
- 否則,當(dāng)前bean放入“正在創(chuàng)建中”列表(singletonsCurrentlyInCreation)
- 使用Factory創(chuàng)建Bean實(shí)例:調(diào)用AbstractAutowireCapableBeanFactory#createBean方法
- 當(dāng)前bean從“正在創(chuàng)建中”列表移出
- 完成創(chuàng)建的Bean從二級(jí)、三級(jí)緩存移出,放入一級(jí)緩存
我們需要注意的是,這個(gè)方法調(diào)用完成之后,一個(gè)完整的bean就被創(chuàng)建出來(lái)、被放入到一級(jí)緩存了。
但你要知道的是,這個(gè)方法不會(huì)那么容易完成的,玄機(jī)就在createBean方法中,如果有依賴(lài)的話(huà)還會(huì)遞歸調(diào)用AbstractBeanFactory#getBean方法的,邏輯又繞回去了…
AbstractAutowireCapableBeanFactory#createBean
真正創(chuàng)建Bean的地方:
- 創(chuàng)建Bean實(shí)例(原始類(lèi)的實(shí)例,或者CGLIB代理對(duì)象實(shí)例)
- 如果是單例Bean并且當(dāng)前Bean正在創(chuàng)建中,則調(diào)用addSingletonFactory:作用是創(chuàng)建一個(gè)工廠方法,添加到三級(jí)緩存。
- 調(diào)用populateBean方法,屬性填充(可能會(huì)發(fā)生循環(huán)依賴(lài)啊…)
需要特別注意的是,三級(jí)緩存就是在這兒創(chuàng)建的,存儲(chǔ)的內(nèi)容是一個(gè)bean factory、使用lamda表達(dá)式創(chuàng)建,工廠方法是getEarlyBeanReference。
getEarlyBeanReference方法后面從三級(jí)緩存獲取對(duì)象的時(shí)候會(huì)回調(diào),作用是通過(guò)SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法最終調(diào)用到wrapIfNecessary方法,目的是判斷bean如果需要AOP的話(huà)則生成他的代理對(duì)象。
三級(jí)緩存存儲(chǔ)bean factory、而不是存儲(chǔ)bean實(shí)例的目的就是為了解決依賴(lài)注入過(guò)程中的代理對(duì)象的處理的。
這部分內(nèi)容確實(shí)有點(diǎn)繞,是需要?jiǎng)觿?dòng)腦子認(rèn)真仔細(xì)思考一下才能搞明白的。
AbstractAutowireCapableBeanFactory#populateBean
屬性填充,這個(gè)過(guò)程中處理自動(dòng)封裝,會(huì)有依賴(lài)注入。
調(diào)用AutowiredAnnotationBeanPostProcessor#postProcessProperties方法。
然后,AutowiredAnnotationBeanPostProcessor#postProcessProperties進(jìn)行屬性賦值。
然后,再調(diào)用DefaultListableBeanFactory#resolveFieldValue方法。
最后,老套路,DefaultListableBeanFactory#doResolveDependency方法。
方法doResolveDependency中首先解決候選注入對(duì)象的問(wèn)題,這部分代碼中包含依賴(lài)注入是查找對(duì)象(DL)的真相,這部分不是今天的主題,跳過(guò)。
DL查找到的肯定不是待注入的對(duì)象,而是待注入對(duì)象的beanName。
那猜想一下,拿到待注入的beanName之后,一定是要拿到bean對(duì)象之后再注入的,Spring會(huì)怎么處理呢?怎么根據(jù)beanName拿到bean對(duì)象呢?
其實(shí)不難猜到!
doResolveDependency方法會(huì)調(diào)用DependencyDescriptor#resolveCandidate方法,resolveCandidate方法調(diào)用beanFactory.getBean獲取對(duì)象,其實(shí)最終調(diào)用的是AbstractBeanFactory的getBean方法。
揉一揉昏花的老眼,是否感覺(jué)到似曾相識(shí)呢?
沒(méi)錯(cuò),往上翻,就是我們開(kāi)始分析循環(huán)依賴(lài)的入口處,又轉(zhuǎn)回去了。
所以沒(méi)錯(cuò),Spring IoC在Bean實(shí)例化、依賴(lài)注入的過(guò)程,就是一個(gè)不斷遞歸調(diào)用的過(guò)程。
小結(jié)
以上,就是Spring IoC的bean的實(shí)例化以及依賴(lài)注入、三級(jí)緩存解決循環(huán)依賴(lài)的主要邏輯了,整理一下、用幾個(gè)例子推導(dǎo)一下,相信就能搞清楚這部分內(nèi)容了。
你如果不相信的話(huà)我們下一篇文章就來(lái)推導(dǎo)一下。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-500531.html
上一篇 Spring FrameWork從入門(mén)到NB -classpath掃描和組件托管文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-500531.html
到了這里,關(guān)于Spring FrameWork從入門(mén)到NB -三級(jí)緩存解決循環(huán)依賴(lài)內(nèi)幕 (一)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!