什么是循環(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ù)完成初始化來提前暴露自身。