詳解Spring循環(huán)依賴
一. 什么是循環(huán)依賴
循環(huán)依賴,就是兩個或則兩個以上的bean互相依賴對方,最終形成閉環(huán)。比如“A對象依賴B對象,而B對象也依賴A對象”,或者“A對象依賴B對象,B對象依賴C對象,C對象依賴A對象”;類似以下代碼:
public class A {
private B b;
}
public class B {
private A a;
}
常規(guī)情況下,會出現(xiàn)以下情況:
- 通過構(gòu)建函數(shù)創(chuàng)建A對象(A對象是半成品,還沒注入屬性和調(diào)用init方法)。
- A對象需要注入B對象,發(fā)現(xiàn)對象池(緩存)里還沒有B對象(對象在創(chuàng)建并且注入屬性和初始化完成之后,會放入對象緩存里)。
- 通過構(gòu)建函數(shù)創(chuàng)建B對象(B對象是半成品,還沒注入屬性和調(diào)用init方法)。
- B對象需要注入A對象,發(fā)現(xiàn)對象池里還沒有A對象。
- 創(chuàng)建A對象,循環(huán)以上步驟。
Spring 循環(huán)依賴的場景有兩種:
- 構(gòu)造器的循環(huán)依賴。
- field 屬性的循環(huán)依賴。
對于構(gòu)造器的循環(huán)依賴,Spring 是無法解決的,只能拋出 BeanCurrentlyInCreationException 異常表示循環(huán)依賴,所以下面我們分析的都是基于 field 屬性的循環(huán)依賴。
Spring 只解決 scope 為 singleton 的循環(huán)依賴。對于scope 為 prototype 的 bean ,Spring 無法解決,直接拋出 BeanCurrentlyInCreationException 異常。
二. 解決循環(huán)依賴
Spring創(chuàng)建Bean的過程中會用到三級緩存:
// DefaultSingletonBeanRegistry.java
/**
* 一級緩存,存放可用的成品Bean。
* 對應(yīng)關(guān)系為 bean name --> bean instance
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* 二級緩存,存放半成品的Bean,半成品的Bean是已創(chuàng)建對象,但是未注入屬性和初始化。用以解決循環(huán)依賴。
* 對應(yīng)關(guān)系也是 bean name --> bean instance。
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/**
* 三級緩存,存的是Bean工廠對象,用來生成半成品的Bean并放入到二級緩存中。用以解決循環(huán)依賴。
* 對應(yīng)關(guān)系是 bean name --> ObjectFactory
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
要了解解決循環(huán)依賴的原理,得從Bean的創(chuàng)建過程說起:
// AbstractBeanFactory.java
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){
//<1> 從緩存中或者實例工廠中獲取 Bean 對象
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
...
}
else {
....
if (mbd.isSingleton()) {
// <3> 將創(chuàng)建好的Bean實例放入一級緩存
sharedInstance = getSingleton(beanName, () -> {
try {
// <2> bean 實例化
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
// 顯式從單例緩存中刪除 Bean 實例
// 因為單例模式下為了解決循環(huán)依賴,可能他已經(jīng)存在了,所以銷毀它。
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
...
}
}
-
<1>首先嘗試從三級緩存中獲取實例 -
<2>如果三級緩存都沒有獲取到bean實例,則說明需要創(chuàng)建該實例,則進行bean的實例化 -
<3>將創(chuàng)建好的實例,放入一級緩存中
2.1 從緩存中獲取Bean
從三級緩存中讀取實例的代碼如下:
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.java
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
// 從單例緩沖中加載 bean(一級緩存)
Object singletonObject = this.singletonObjects.get(beanName);
// 緩存中的 bean 為空,且當前 bean 正在創(chuàng)建
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 二級緩存
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果earlySingletonObjects 中沒有,且允許提前創(chuàng)建
if (singletonObject == null && allowEarlyReference) {
// 加鎖(雙重校驗鎖模式)
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
// 拿到鎖后再次獲取單例實例
singletonObject = this.singletonObjects.get(beanName);
// 如果單例實例仍然為空
if (singletonObject == null) {
// 并且 早期單例對象的緩存 中也為空
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 從 singletonFactories 中獲取對應(yīng)的 ObjectFactory(三級緩存)
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//<1.1>
// 獲得 bean
singletonObject = singletonFactory.getObject();
// 添加 bean 到 早期單例對象的緩存 中
this.earlySingletonObjects.put(beanName, singletonObject);
// 從 singletonFactories 中移除對應(yīng)的 ObjectFactory
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
<1.1>處,如果從三級緩存中獲取到實例后,就會放入二級緩存,并刪除三級緩存。
2.2 實例化Bean
<2>處,如果三級緩存都沒有獲取到bean實例,則說明需要創(chuàng)建該實例,則進行bean的實例化,代碼如下:
//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.java
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
....
try {
// <2.1> 創(chuàng)建 Bean 對象
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
....
}
調(diào)用doCreateBean方法創(chuàng)建實例:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...
// <2.1.1> 使用合適的實例化策略來創(chuàng)建新的實例:工廠方法、構(gòu)造函數(shù)自動注入、簡單初始化
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
...
// <2.1.2> 判斷是否有后置處理
// 如果有后置處理,則允許后置處理修改 BeanDefinition
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// 后置處理修改 BeanDefinition
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
// <2.1.3> 解決單例模式的循環(huán)依賴
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 提前將創(chuàng)建的 bean 實例加入到 singletonFactories(三級緩存) 中
// 這里是為了后期避免循環(huán)依賴
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 開始初始化 bean 實例對象
Object exposedObject = bean;
try {
// <2.1.4> 對 bean 進行填充,將各個屬性值注入,其中,可能存在依賴于其他 bean 的屬性
// 則會遞歸初始依賴 bean
populateBean(beanName, mbd, instanceWrapper);
// <2.1.5> 調(diào)用初始化方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
...
return exposedObject;
}
該方法整體流程比較復(fù)雜,詳細邏輯可以參考 《創(chuàng)建Bean的流程》,但是整體流程如下:
<2.1.1>使用合適的實例化策略來創(chuàng)建新的實例:工廠方法、構(gòu)造函數(shù)自動注入、簡單初始化<2.1.2>后置處理<2.1.3>判斷是否提前曝光,如果提前曝光,則將當前Bean的ObjectFactory放入三級緩存中。<2.1.4>對 bean 進行填充,其中,如果依賴于其他 bean 的屬性,則會遞歸調(diào)用getBean創(chuàng)建依賴的實例。<2.1.5>調(diào)用初始化方法
解決循環(huán)依賴非常關(guān)鍵的一步就是<2.1.3>,將當前剛創(chuàng)建好的Bean實例(未初始化),封裝成ObjectFactory,放入三級緩存中:
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
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);
}
}
}
2.3 將創(chuàng)建好的Bean實例放入一級緩存
<3>處,將創(chuàng)建好的實例,放入一級緩存中,代碼如下:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 全局鎖
synchronized (this.singletonObjects) {
...
try {
// <3.1> 初始化 bean
// 這個過程其實是調(diào)用 createBean() 方法(策略模式,具體的創(chuàng)建邏輯由傳入的ObjectFactory決定)
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
...
// <3.2> 加入一級緩存中
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
return singletonObject;
}
}
<3.1>,調(diào)用<2>處的代碼創(chuàng)建實例,詳見 《2.2 實例化Bean》<3.2>,由于<2>處的代碼創(chuàng)建的實例已經(jīng)是初始化完成的,所以在此處將Bean實例加入一級緩存中。
2.4 整體流程

2.5 循環(huán)依賴流程
我們以最簡單的循環(huán)依賴為例,A、B兩個類都互相依賴對方:
public class A {
private B b;
}
public class B {
private A a;
}
假如Spring容器啟動時,先加載A,那么A、B循環(huán)依賴初始化流程如下圖所示:

本文參考至:
Spring為什么不使用二級緩存?Spring 動態(tài)代理時是如何解決循環(huán)依賴的?為什么要使用三級緩存?… - 知乎 (zhihu.com)

浙公網(wǎng)安備 33010602011771號