Spring是如何解決setter迴圈注入的

2021-10-01 08:38:56 字數 4790 閱讀 4400

對於該問題,相信不少人面試中遇到過,原始碼也去看過,這裡我想說的是,看原始碼的方式。

單純的去看原始碼是毫無意義的,看完了就忘了,最多也只是零星記得有那麼些東西,我覺得要想做的真正理解還是要帶著問題去看。

假設現在有a、b兩個類,a、b相互迴圈依賴,那麼我們就帶著這個問題去找

首先getbean(a)必然回去例項化a,由於第一次建立a,那麼就不存在快取,直接進入abstractautowirecapablebeanfactory#docreatebean方法,也是建立bean的核心方法

protected object docreatebean(final string beanname, final rootbeandefinition mbd, final @nullable object args)

throws beancreationexception

} //獲取例項化物件的型別

if (beantype != nullbean.class)

// allow post-processors to modify the merged bean definition.

//呼叫postprocessor後置處理器

synchronized (mbd.postprocessinglock)

catch (throwable ex)

mbd.postprocessed = true;

}} // eagerly cache singletons to be able to resolve circular references

// even when triggered by lifecycle inte***ces like beanfactoryaware.

//向容器中快取單例模式的bean物件,以防迴圈引用

boolean earlysingletonexposure = (mbd.issingleton() && this.allowcircularreferences &&

issingletoncurrentlyincreation(beanname));

if (earlysingletonexposure)

//這裡是乙個匿名內部類,為了防止迴圈引用,盡早持有物件的引用

addsingletonfactory(beanname, () -> getearlybeanreference(beanname, mbd, bean));

} // initialize the bean instance.

//bean物件的初始化,依賴注入在此觸發

//這個exposedobject在初始化完成之後返回作為依賴注入完成後的bean

object exposedobject = bean;

try

catch (throwable ex)

else

} if (earlysingletonexposure)

//當前bean依賴其他bean,並且當發生迴圈引用時不允許新建立例項物件

string dependentbeans = getdependentbeans(beanname);

setactualdependentbeans = new linkedhashset<>(dependentbeans.length);

//獲取當前bean所依賴的其他bean

for (string dependentbean : dependentbeans)

}if (!actualdependentbeans.isempty()) }}

} // register bean as disposable.

//註冊完成依賴注入的bean

try

catch (beandefinitionvalidationexception ex)

return exposedobject;

}

執行依賴注入之前會先執行addsinglefactory方法,將該bean對應的objectfactory新增到map> singletonfactories容器中,下邊執行依賴注入方法populatebean,因為我們a中有個b屬性,所以最終肯定是要去執行getbean(b) 的,同樣也會進入我們的docreatebean方法,也會執行populatebean想把a注入進來,這個時候會執行getbean(a),但是不會再走docreatebean方法了,會在一開始的defaultsingletonbeanregistry#getsingleton(string beanname)讀取快取,會使用我們singletonfactories中快取的a的singletonfactory.getobject()方法得到singletonobject,並且將該物件再放入另乙個快取當中mapearlysingletonobjects。到這裡,我們的b其實可以進行初始化了,但是b雖然注入了a,但是這個a是個原始物件,也就是a例項化之前注入到b的。

在b例項化之後來到外層的dogetbean方法

protected t dogetbean(final string name, @nullable final classrequiredtype,

@nullable final object args, boolean typecheckonly) throws bean***ception

else

}//獲取給定bean的例項物件,主要是完成factorybean的相關處理

//注意:beanfactory是管理容器中bean的工廠,而factorybean是

//建立建立物件的工廠bean,兩者之間有區別

bean = getobjectforbeaninstance(sharedinstance, name, beanname, null);

} else

// check if bean definition exists in this factory.

//對ioc容器中是否存在指定名稱的beandefinition進行檢查,首先檢查是否

//能在當前的beanfactory中獲取的所需要的bean,如果不能則委託當前容器

//的父級容器去查詢,如果還是找不到則沿著容器的繼承體系向父級容器查詢

beanfactory parentbeanfactory = getparentbeanfactory();

//當前容器的父級容器存在,且當前容器中不存在指定名稱的bean

if (parentbeanfactory != null && !containsbeandefinition(beanname))

else if (args != null)

else

}//建立的bean是否需要進行型別驗證,一般不需要

if (!typecheckonly)

try

//遞迴呼叫getbean方法,獲取當前bean的依賴bean

registerdependentbean(dep, beanname);

//把被依賴bean註冊給當前依賴的bean

getbean(dep);}}

// create bean instance.

//建立單例模式bean的例項物件

if (mbd.issingleton())

catch (bean***ception ex)

});//獲取給定bean的例項物件

bean = getobjectforbeaninstance(sharedinstance, name, beanname, mbd);

}//ioc容器建立原型模式bean例項物件

else if (mbd.isprototype())

finally

//獲取給定bean的例項物件

bean = getobjectforbeaninstance(prototypeinstance, name, beanname, mbd);

}//要建立的bean既不是單例模式,也不是原型模式,則根據bean定義資源中

//配置的生命週期範圍,選擇例項化bean的合適方法,這種在web應用程式中

else

try

finally

});//獲取給定bean的例項物件

bean = getobjectforbeaninstance(scopedinstance, name, beanname, mbd);

}catch (illegalstateexception ex) }}

catch (bean***ception ex)

}

這個方法比較長,我們主要關注建立單例這裡

if (mbd.issingleton()) 

catch (bean***ception ex)

});//獲取給定bean的例項物件

bean = getobjectforbeaninstance(sharedinstance, name, beanname, mbd);

}

這裡的defaultsingletonbeanregistry#getsingleton(string beanname, objectfactory<?> singletonfactory)裡面的addsingleton(beanname, singletonobject)會將earlysingletonobjects裡的物件放到singletonobjects裡面。至此,b已經例項化完畢了,並且存到了剛剛提到的這個容器裡面

這時候a會拿到例項化後的b進行依賴注入,初始化,而beanb最初拿到的原始物件指向的還是beana,隨著a被初始化也能正常用了,這就解決了迴圈依賴。

Spring 是如何 解決迴圈依賴的問題

初次遇到這個問題是在開發中,但是沒有深究,前一陣參加面試就被問到這個問題,當時真是非常後悔,怎麼沒有好好研究一下呢。現在來亡羊補牢吧。迴圈依賴的定義 迴圈依賴就是迴圈引用,就是兩個或多個bean 相互之間的持有對方,比如circlea 引用circleb circleb 引用circlec,circ...

Spring如何解決迴圈依賴

比如 在a類引入b物件,在b類引入a物件,建立a的時候需要依賴b,建立b的時候需要依賴a,而各自建立物件的時候,其互相依賴的物件還沒有建立完成,就導致各自都無法成功建立物件。這就是迴圈依賴。class aclass b解決迴圈依賴的方法很簡單,如下所示,先例項化a和b,此時都沒初始化,即都沒有對各自...

spring如何解決迴圈依賴

1.構造器注入 無法解決 構造器注入時,a,b都沒有進行初始化物件 構造方法無法執行完成 2.spring 為了解決單例的迴圈依賴問題,使用了 快取。其中一級快取為單例池 singletonobjects 二級快取為提前 物件 earlysingletonobjects 快取為提前 物件工廠 sin...