Spring 如何解決 Bean 循環(huán)依賴問題

什么是循環(huán)依賴問題

循環(huán)依賴其實就是循環(huán)引用娱挨,也就是兩個或則兩個以上的bean互相持有對方诽凌,最終形成閉環(huán)。比如A依賴于B舍扰,B依賴于C倦蚪,C又依賴于A。

Spring 如何解決循環(huán)依賴問題

簡而言之Spring使用三級緩存來解決循環(huán)依賴的問題边苹,三級緩存如下所示:

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);


/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);


/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

三級緩存分別為

  • singletonObjects:單例Bean緩存陵且;

  • singletonFactories:單例Bean工廠緩存;

  • earlySingletonObjects:提前曝光的單例Bean緩存个束;

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

獲取單例Bean的大致過程如下:

  • 首先從一級緩存singletonObjects中通過beanName獲取單例bean慕购;
  • 若一級緩存中不存在該bean,并且isSingletonCurrentlyInCreation返回為true茬底,那么通過earlySingletonObjects獲取提前曝光的bean沪悲;
  • 若二級緩存中不存在該bean,并且允許通過singletonFactories獲取對象阱表,那么通過工廠來創(chuàng)建bean可训。

isSingletonCurrentlyInCreation表示當前bean是否正在創(chuàng)建中,例如對象A通過構(gòu)造函數(shù)依賴B捶枢,獲取對象A在填充屬性的過程中,發(fā)現(xiàn)通過成員變量的形式依賴對象B飞崖,那么需要先對B進行實例化烂叔,這種情況下,對象A的狀態(tài)就是在正在創(chuàng)建中固歪。

要理解Spring如何解決循環(huán)依賴問題蒜鸡,還需要了解Spring Bean的初始化過程胯努,spring bean 的初始化大致可以分為如下過程

  • 調(diào)用構(gòu)造方法進行實例化;
  • 填充對象屬性逢防;
  • 通過init方法進行初始化

創(chuàng)建Bean的過程中叶沛,完成第一步驟,對象已經(jīng)被生產(chǎn)出來了忘朝,只是對象缺失了一些屬性灰署,但是對象已經(jīng)能夠被引用了。

解決循環(huán)依賴的關(guān)鍵就是三級緩存局嘁,其中三級緩存singletonFactories是在方法addSingletonFactory中進行填充的溉箕,

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            if (!this.singletonObjects.containsKey(beanName)) {
                this.singletonFactories.put(beanName, singletonFactory);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }
    }

該方法被AbstractAutowireCapableBeanFactory類中的doCreateBean調(diào)用,那么在bean實例化之后悦昵,即調(diào)用了構(gòu)造函數(shù)之后肴茄,singletonFactories已經(jīng)被注冊,通過singletonFactories但指,spring將未完整初始化的對象暴露出來寡痰,供其他類使用。

若對象A通過成員變量的形式依賴對象B棋凳,對象B通過循環(huán)依賴的形式依賴對象A拦坠,我們來看一下Spring解決循環(huán)依賴的步驟

  • 對象A進行初始化,首先通過構(gòu)造函數(shù)進行實例化贫橙,然后填充屬性贪婉,發(fā)現(xiàn)依賴的對象B沒有進行加載;
  • 對象B進行初始化卢肃,首先通過構(gòu)造函數(shù)進行實例化疲迂,然后填充屬性,此時對象A以被提前暴露出來莫湘,對象B完成初始化尤蒿;
  • 對象A通過對象B完成屬性填充,完成初始化幅垮。

通過spring解決循環(huán)依賴的原理腰池,可以看出,對于相互通過構(gòu)造函數(shù)產(chǎn)生的循環(huán)依賴是無法解決的忙芒,如對象A通過構(gòu)造函數(shù)依賴對象B示弓,同時對象B通過構(gòu)造函數(shù)依賴對象A,此時Spring就無能為力了呵萨,因為對象A和對象B奏属,都無法通過構(gòu)造函數(shù)完成初始化來提前暴露自身。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末潮峦,一起剝皮案震驚了整個濱河市囱皿,隨后出現(xiàn)的幾起案子勇婴,更是在濱河造成了極大的恐慌,老刑警劉巖嘱腥,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耕渴,死亡現(xiàn)場離奇詭異,居然都是意外死亡齿兔,警方通過查閱死者的電腦和手機橱脸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愧驱,“玉大人慰技,你說我怎么就攤上這事∽檠猓” “怎么了吻商?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長糟红。 經(jīng)常有香客問我艾帐,道長,這世上最難降的妖魔是什么盆偿? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任柒爸,我火速辦了婚禮,結(jié)果婚禮上事扭,老公的妹妹穿的比我還像新娘捎稚。我一直安慰自己,他們只是感情好求橄,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布今野。 她就那樣靜靜地躺著,像睡著了一般罐农。 火紅的嫁衣襯著肌膚如雪条霜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天涵亏,我揣著相機與錄音宰睡,去河邊找鬼。 笑死气筋,一個胖子當著我的面吹牛拆内,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播宠默,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼矛纹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了光稼?” 一聲冷哼從身側(cè)響起或南,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎艾君,沒想到半個月后采够,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡冰垄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年蹬癌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虹茶。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡逝薪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蝴罪,到底是詐尸還是另有隱情董济,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布要门,位于F島的核電站虏肾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏欢搜。R本人自食惡果不足惜封豪,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望炒瘟。 院中可真熱鬧吹埠,春花似錦、人聲如沸疮装。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斩个。三九已至胯杭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間受啥,已是汗流浹背做个。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留滚局,地道東北人居暖。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像藤肢,于是被迫代替她去往敵國和親太闺。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354