Spring源碼解析——循環(huán)依賴的解決方案

一稀拐、前言

承接《Spring源碼解析——?jiǎng)?chuàng)建bean》《Spring源碼解析——?jiǎng)?chuàng)建bean的實(shí)例》飞崖,我們今天接著聊聊烂叔,循環(huán)依賴的解決方案,即創(chuàng)建bean的ObjectFactory固歪。

二蒜鸡、ObjectFactory

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    if (logger.isDebugEnabled()) {
        logger.debug("Eagerly caching bean '" + beanName +
                "' to allow for resolving potential circular references");
    }
    // 為避免后期循環(huán)依賴,可以在bean初始化完成前將創(chuàng)建實(shí)例的ObjectFactory加入工廠
    /**
     * getEarlyBeanReference(beanName, mbd, bean)方法:
     * 對bean再一次依賴引用牢裳,主要應(yīng)用SmartInstantiationAwareBeanPostProcessor
     * 其中我們熟知的AOP就是在這里將advice動(dòng)態(tài)織入bean中逢防,若沒有則直接返回bean,不做任何處理
     */
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

這段代碼不是很復(fù)雜蒲讯,但是很多人不是太理解這段代碼的作用忘朝,而且,這段代碼僅從此函數(shù)中去理解也很難弄懂其中的含義伶椿,我們需要從全局的角度去思考 Spring 的依賴解決辦法辜伟。
earlySingletonExposure :從字面的意思理解就是提早曝光的單例,我們暫不定義它的學(xué)名叫什么脊另,我們感興趣的是有哪些條件影響這個(gè)值导狡。

  • mbd.isSingleton() :沒有太多可以解釋的,此 RootBeanDefinition 代表的是否是單例偎痛。
  • this.allowCircularReferences :是否允許循環(huán)依賴旱捧,很抱歉,并沒有找到在配置文件中如何配置,但是在 AbstractRefreshableApplicationContext 中提供了設(shè)置函數(shù)枚赡,可以通過硬編碼的方式進(jìn)行設(shè)置或者可以通過自定義命名空間進(jìn)行配置氓癌,其中硬編碼的方式代碼如下。
ClassPathXmlApplicationContext bf = ClassPathXmlApplicationContext("aspectTest.xml" ); bt.setAllowBeanDefinitionOverriding(false);
  • isSingletonCurrentlylncreation(beanName) :該 bean 是否在創(chuàng)建中贫橙。在 Spring 中贪婉,會(huì)有個(gè)專門的屬性默認(rèn)為 DefaultSingletonBeanRegistry的 singletonsCurrentlylnCreation 來記錄 bean 的加載狀態(tài),在 bean 開始創(chuàng)建前會(huì)將 beanName 記錄在屬性中卢肃,在 bean 創(chuàng)建結(jié)束后會(huì)將 beanName 從屬性中移除疲迂。那么我們跟隨代碼一路走來可是對這個(gè)屬性的記錄并沒有多少印象,這個(gè)狀態(tài)是在哪里記錄的呢莫湘?不同 scope 的記錄位置并不一樣尤蒿,我們以 singleton 為例,在 singleton 下記錄屬性的函數(shù)是在 DefaultSingletonBeanRegistry的 public Object getSingleton(String beanName, ObjectFactory singletonFactory)函數(shù)的 beforeSingletonCreation(beanName)和 afterSingletonCreation(beanName)中幅垮,在這兩段函數(shù)中分別this.singletonCurrentlylnCreation.add(beanName)與 this.singletonCurrentlylnCreation.remove(beanName)來進(jìn)行狀態(tài)的記錄與移除腰池。

經(jīng)過以上分析我們了解變量 earl earlySingletonExposure 是否是單例、是否允許循環(huán)依賴忙芒、是否對應(yīng)的 bean 正在創(chuàng)建的條件的綜合示弓。當(dāng)這 3 個(gè)條件都滿足時(shí)會(huì)執(zhí)行 addSingletonFactory操作,那么加入 SingletonFactory的作用是什么呢匕争?又是在什么時(shí)候調(diào)用呢避乏?

我們還是以最簡單的AB循環(huán)依賴為例,類A中含有屬性類B甘桑,而類B中又會(huì)含有屬性類A拍皮,那么初始化beanA的過程如下圖所示:


上圖展示了創(chuàng)建 beanA 的流程,圖中我們看到跑杭,在創(chuàng)建 A 的時(shí)候首先會(huì)記錄類 A 所對應(yīng)的 beanName铆帽,并將beanA的創(chuàng)建工廠加入緩存中,而在對 A的屬性填充也就是調(diào)用populate方法的時(shí)候又會(huì)再一次的對 B 進(jìn)行遞歸創(chuàng)建德谅。同樣的爹橱,因?yàn)樵?B 中同樣存在 A 屬性,因此在實(shí)例化 B 的的 populate 方法中又會(huì)再次地初始化 A 窄做,也就是圖形的最后愧驱,調(diào)用 getBean(A)。關(guān)鍵是在這里椭盏,有心的同學(xué)可以去找找這個(gè)代碼的實(shí)現(xiàn)方式组砚,我們之前已經(jīng)講過,在這個(gè)函數(shù)中并不是直接去實(shí)例化 A 掏颊,而是先去檢測緩存中是否有已經(jīng)創(chuàng)建好的對應(yīng)的 bean 糟红,或者是否已經(jīng)創(chuàng)建好的 ObjectFactory艾帐,而此時(shí)對于A的 ObjectFactory我們早已經(jīng)創(chuàng)建,所以便不會(huì)再去向后執(zhí)行盆偿,而是直接調(diào)用 ObjectFactory去創(chuàng)建 A 柒爸。這里最關(guān)鍵的是 ObjectFactory的實(shí)現(xiàn)。

/**
 * getEarlyBeanReference(beanName, mbd, bean)方法:
 * 對bean再一次依賴引用事扭,主要應(yīng)用SmartInstantiationAwareBeanPostProcessor
 * 其中我們熟知的AOP就是在這里將advice動(dòng)態(tài)織入bean中捎稚,若沒有則直接返回bean,不做任何處理
 */
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

其中g(shù)etEarlyBeanReference的代碼如下:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

在 getEarlyBeanReference 函數(shù)中并沒有太多的邏輯處理句旱,或者說除了后處理器的調(diào)用外沒有別的處理工作阳藻,根據(jù)以上分析,基本可以理清 spring 處理循環(huán)依賴的解決辦法谈撒,在 B 中創(chuàng)建依賴 A 時(shí)通過 ObjectFactory 提供的實(shí)例化方法來中斷 A 中的屬性填充,使 B 中持有的 A 僅僅是剛剛初始化并沒有填充任何屬性的 A 匾南,而這正初始化 A 的步驟還是在最開始創(chuàng)建 A 的時(shí)候進(jìn)行的啃匿,但是因?yàn)?A 與 B 中的 A 所表示的屬性地址是一樣的,所以在 A 中創(chuàng)建好的屬性填充自然可以通過 B 中的 A 獲取蛆楞,這樣就解決了循環(huán)依賴的問題溯乒。

三、小結(jié)

大體上的原理就是這樣豹爹,有什么不明白的地方裆悄,大伙可繼續(xù)閱讀如下文章:

https://blog.csdn.net/m0_38043362/article/details/80284577

https://blog.csdn.net/hzcao/article/details/78479593

http://book.51cto.com/art/201311/419098.htm

本文在米兜公眾號(hào)鏈接

https://mp.weixin.qq.com/s/P7f0HLnyjHqoN4-rUm0ytQ

歡迎關(guān)注米兜Java,一個(gè)注在共享臂聋、交流的Java學(xué)習(xí)平臺(tái)光稼。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市孩等,隨后出現(xiàn)的幾起案子艾君,更是在濱河造成了極大的恐慌,老刑警劉巖肄方,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冰垄,死亡現(xiàn)場離奇詭異,居然都是意外死亡权她,警方通過查閱死者的電腦和手機(jī)虹茶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隅要,“玉大人蝴罪,你說我怎么就攤上這事∈搬悖” “怎么了洲炊?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我暂衡,道長询微,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任狂巢,我火速辦了婚禮撑毛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘唧领。我一直安慰自己藻雌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布斩个。 她就那樣靜靜地躺著胯杭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪受啥。 梳的紋絲不亂的頭發(fā)上做个,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天,我揣著相機(jī)與錄音滚局,去河邊找鬼居暖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛藤肢,可吹牛的內(nèi)容都是我干的太闺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嘁圈,長吁一口氣:“原來是場噩夢啊……” “哼省骂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起丑孩,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤冀宴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后温学,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體略贮,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年仗岖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了逃延。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡轧拄,死狀恐怖揽祥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情檩电,我是刑警寧澤拄丰,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布府树,位于F島的核電站,受9級(jí)特大地震影響料按,放射性物質(zhì)發(fā)生泄漏奄侠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一载矿、第九天 我趴在偏房一處隱蔽的房頂上張望垄潮。 院中可真熱鬧,春花似錦闷盔、人聲如沸弯洗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽牡整。三九已至,卻和暖如春溺拱,著一層夾襖步出監(jiān)牢的瞬間果正,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工盟迟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人潦闲。 一個(gè)月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓攒菠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親歉闰。 傳聞我的和親對象是個(gè)殘疾皇子辖众,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評論 2 354

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