最近面試的時(shí)候發(fā)現(xiàn)很多人會(huì)問(wèn)Spring是如何解決循環(huán)依賴(lài)的,雖然知道是通過(guò)三級(jí)緩存去解決的,但是也僅僅只是知其然,不知其所以然,抱著學(xué)習(xí)的心態(tài)還是好好捋一捋:
- 三級(jí)緩存是如何解決循環(huán)依賴(lài)的?
- 為什么是三級(jí)緩存?二級(jí)緩存行不行?
- 有什么好的方式可以避免構(gòu)建IOC的時(shí)候產(chǎn)生循環(huán)依賴(lài)?
循環(huán)依賴(lài)的場(chǎng)景
這個(gè)場(chǎng)景其實(shí)分為很多種:
簡(jiǎn)單一點(diǎn)場(chǎng)景: A -> B -> A
復(fù)雜一點(diǎn)的場(chǎng)景:
- A 依賴(lài) B,C
- B依賴(lài)A
- C依賴(lài)A
在我們業(yè)務(wù)邏輯越來(lái)越復(fù)雜的時(shí)候,難免因?yàn)閷蛹?jí)過(guò)深導(dǎo)致這種場(chǎng)景出現(xiàn),但是在沒(méi)有運(yùn)行的時(shí)候發(fā)現(xiàn)不了。
另外Spring是能夠解決set屬性賦值的循環(huán)依賴(lài),但是構(gòu)造器注入的是會(huì)有問(wèn)題的,構(gòu)造器在實(shí)例化的時(shí)候會(huì)出現(xiàn)死結(jié),而set可以預(yù)先實(shí)例化后賦值所以好解決。
三級(jí)依賴(lài)是如何解決的?
首先我們要了解三級(jí)緩存的用處:
- 一級(jí)緩存
singletonObjects: 用于保存實(shí)例化、注入、初始化完成的bean實(shí)例。
這里就是生命周期已經(jīng)加載完成了的對(duì)象
- 二級(jí)緩存
earlySingletonObjects: 用于保存實(shí)例化完成的bean實(shí)例.
其實(shí)也就是new完了的對(duì)象,但是沒(méi)有進(jìn)行set(依賴(lài)注入)、以及初始化的對(duì)象,就是簡(jiǎn)單的實(shí)例化對(duì)象。
- 三級(jí)緩存
singletonFactories: 用于保存bean創(chuàng)建ObjectFactory工廠(chǎng),方便后續(xù)可以創(chuàng)建代理對(duì)象。
這里很重要,這個(gè)工廠(chǎng)里面會(huì)包含bean的創(chuàng)建,可能是普通對(duì)象,可能是代理過(guò)后的對(duì)象??梢岳斫鉃橄劝裯ew完之后的實(shí)例引用先獲取到。
循環(huán)依賴(lài)場(chǎng)景: A -> B -> A
了解大概流程,先不糾結(jié)于細(xì)節(jié)。

緩存獲取順序的代碼:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 先從一級(jí)緩存中獲取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
synchronized(this.singletonObjects) {
// 然后從二級(jí)緩存里面獲取對(duì)象
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 最后從三級(jí)緩存中獲取對(duì)象
ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 從工廠(chǎng)里面獲取對(duì)應(yīng)的值,可能是普通實(shí)例,可能是代理對(duì)象
singletonObject = singletonFactory.getObject();
// 放入二級(jí)緩存
this.earlySingletonObjects.put(beanName, singletonObject);
// 刪除三級(jí)緩存
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
這里有個(gè)很重要的點(diǎn):就是當(dāng)B要獲取A的時(shí)候,從三級(jí)緩存里面查找,這時(shí)候已經(jīng)能夠找到了,就會(huì)從ObjectFactory工廠(chǎng)中返回一個(gè)對(duì)象,這個(gè)對(duì)象可能是普通實(shí)例也可能是代理對(duì)象。 這個(gè)時(shí)候會(huì)加入到二級(jí)緩存中,下一次查找就能從一級(jí),然后到二級(jí)直接找到對(duì)象了,不會(huì)在走到三級(jí)封裝成ObjectFactory對(duì)象了(每次從工廠(chǎng)里面拿可能會(huì)不是同一個(gè)實(shí)例)。
也就是說(shuō),發(fā)生循環(huán)的時(shí)候,會(huì)從工廠(chǎng)中將對(duì)象提前實(shí)例化出來(lái),然后這個(gè)引用會(huì)被會(huì)注入到發(fā)生循環(huán)依賴(lài)的Bean作為屬性填充。
另外下面的代碼是創(chuàng)建bean中,會(huì)預(yù)先將bean封裝成ObjectFactory對(duì)象的代碼
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
// addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// .. 省略
// 加入三級(jí)緩存中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
Object exposedObject = bean;
try {
// 然后開(kāi)始對(duì)該bean進(jìn)行屬性賦值
populateBean(beanName, mbd, instanceWrapper);
// 執(zhí)行init方法.初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
}
return exposedObject;
// .. 省略
}
// 封裝之后,加入三級(jí)緩存.
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 先加入三級(jí)緩存
this.singletonFactories.put(beanName, singletonFactory);
// 刪除二級(jí)緩存
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference
// 這里是ObjectFactory對(duì)象構(gòu)建并獲取的邏輯
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
// 這里會(huì)提前執(zhí)行后置處理器,有可能會(huì)返回的是一個(gè)代理對(duì)象
for (BeanPostProcessor bp : getBeanPostProcessors()) {
// 通過(guò)這個(gè)SmartInstantiationAwareBeanPostProcessor類(lèi)型的執(zhí)行器,來(lái)獲取提前暴露對(duì)象的邏輯
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
// 當(dāng)實(shí)例創(chuàng)建完成之后,會(huì)加入到單例工廠(chǎng),從二級(jí)緩存升級(jí)到一級(jí)緩存中
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
這個(gè)時(shí)候B初始化好了直接加入到一級(jí)緩存中,而A在三級(jí)緩存被查找到后,放入二級(jí)緩存中,下一次查找會(huì)直接從二級(jí)緩存升級(jí)到一級(jí)緩存。流程結(jié)束。
我估計(jì)你現(xiàn)在還是會(huì)有點(diǎn)疑問(wèn):
從ObjectFactory中獲取的對(duì)象確實(shí)是提前暴露實(shí)例化的對(duì)象,但是它是咋進(jìn)行屬性填充的?
還是看下圖吧:

我不知道注釋的代碼你能否理解...
總的來(lái)說(shuō)就是引用邏輯,先將引用傳遞到工廠(chǎng)中,讓工廠(chǎng)構(gòu)建早期bean的時(shí)候是基于這個(gè)實(shí)例的引用去做的,這個(gè)時(shí)候依賴(lài)注入給其他Bean的時(shí)候也是基于該引用,所以等到該bean初始化完成,其他被其依賴(lài)注入的bean的引用就是初始化完成的。
為什么要三級(jí)緩存? 二級(jí)緩存行不行?
從上面的流程上來(lái)看二級(jí)緩存只是為了將工廠(chǎng)得到的實(shí)例對(duì)象預(yù)先存儲(chǔ)在二級(jí)緩存中,作用也不是特別明顯。
但是首先要思考一個(gè)問(wèn)題:假設(shè)干掉二級(jí)緩存,三級(jí)變成兩級(jí)。
假設(shè)循環(huán)依賴(lài)的場(chǎng)景是: A->B->C->B->A

TestService1注入到TestService3又需要從第三級(jí)緩存中獲取實(shí)例,而第三級(jí)緩存里保存的并非真正的實(shí)例對(duì)象,而是ObjectFactory對(duì)象。
說(shuō)白了,兩次從三級(jí)緩存中獲取都是ObjectFactory對(duì)象,而通過(guò)它創(chuàng)建的實(shí)例對(duì)象每次可能都不一樣的,比如代理對(duì)象,每次獲取的都是一個(gè)新的代理對(duì)象。
為了解決這個(gè)問(wèn)題,spring引入的第二級(jí)緩存。上面圖1其實(shí)TestService1對(duì)象的實(shí)例已經(jīng)被添加到第二級(jí)緩存中了,而在TestService1注入到TestService3時(shí),只用從第二級(jí)緩存中獲取該對(duì)象即可。
所以二級(jí)緩存還是有必要的。區(qū)分工廠(chǎng)獲取的對(duì)象和具體實(shí)例的引用對(duì)象。

還有個(gè)問(wèn)題,第三級(jí)緩存中為什么要添加ObjectFactory對(duì)象,直接保存實(shí)例對(duì)象不行嗎?
答:不行,因?yàn)榧偃缒阆雽?duì)添加到三級(jí)緩存中的實(shí)例對(duì)象進(jìn)行增強(qiáng),直接用實(shí)例對(duì)象是行不通的。
直接反射實(shí)例化的話(huà),沒(méi)辦法經(jīng)過(guò)SpringBean的后置處理器參考getEarlyBeanReference方法生成增強(qiáng)對(duì)象。
有什么好的方式避免循環(huán)依賴(lài)嗎?
比如我明顯知道A 依賴(lài) B 了,這個(gè)時(shí)候B也需要A。
我們可以采用懶加載的方式以及容器先加載完的方式再獲取.
- @Layz注解,延遲加載
- B實(shí)現(xiàn)
ApplicationContextAware的接口獲取到上下文,然后從上下文中獲取A,總的來(lái)說(shuō)也是懶加載的思路.
好了,以上僅僅是本人在遇到這個(gè)問(wèn)題的一些延伸及參考加思考所寫(xiě),感謝你這么忙還能觀(guān)看我的文章。
如果有問(wèn)題歡迎留言交流,我會(huì)及時(shí)回復(fù),希望共同進(jìn)步。