概念
如果 class A 中依賴了 class B并且class B 中也依賴了class A,形成一個閉環就會産生循環依賴的問題。
解決
構造器注入方式的循環依賴,無法解決;
Setter注入方式的循環依賴,解決方式:
- Spring先用構造器執行個體化Bean對象,将執行個體化結束的對象放到一個Map中,并且Spring提供擷取這個未設定屬性的執行個體化對象的引用方法;
- 在進行屬性注入的時候,依次擷取到對應的Bean執行個體對象進行注入;
原理
在 AbstractBeanFactory 的 doGetBean 方法中,會首先通過緩存中去查找單例 Bean 對象。調用 getSingleton 方法,源碼如下:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//檢查緩存中是否存在執行個體
Object singletonObject = this.singletonObjects.get(beanName);
//如果單例bean為空但工廠内正在加載
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//如果為空,則鎖定全局變量并進行處理
synchronized (this.singletonObjects) {
//如果此bean正在加載則不處理
singletonObject = this.earlySingletonObjects.get(beanName);
//是否允許從提前曝光緩存singletonFactories中通過getObject拿到對象,解決屬性注入循環依賴問題的關鍵
if (singletonObject == null && allowEarlyReference) {
//當某些方法需要提前初始化的時候則會調用addSingletonFactory方法将對應的ObjectFactory初始化政策存儲在singletonFactories
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//調用預先設定的getObject方法
singletonObject = singletonFactory.getObject();
//記錄在緩存中,earlySingletonObjects和singletonFactories互斥
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
從這個方法上我們可以看到 Spring 使用三級緩存解決循環依賴問題,每個緩存存儲執行個體對象引用的不同生命周期:
- singletonFactories : 單例對象工廠的cache,緩存第一步執行個體化後的對象
- earlySingletonObjects :提前曝光的單例對象的Cache ,緩存第二步填充屬性後的對象(用于檢測循環引用,與singletonFactories互斥)
- singletonObjects:單例對象的cache,緩存第三步初始化完成後的對象
在通過上一篇Spring 之 DI 詳解,我們知道 Bean 初始化的三個重要步驟為:
- 調用 createBeanInstance 方法進行執行個體化對象
- 調用 populateBean 方法進行依賴注入
- 調用 initializeBean 方法執行 BeanPostProcessor 後置處理器
我們來看看執行個體化完之後在什麼時候存儲到 singletonFactories 緩存中,在 AbstractAutowireCapableBeanFactory.doCreateBean() 方法中。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
//封裝被建立的Bean對象
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//根據指定bean使用對應的政策建立執行個體對象,如:指定的工廠方法、根據參數選擇構造函數、預設無參構造方法
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//...
// 向容器中緩存單例模式的Bean對象,以防止循環引用
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");
}
//為避免後期循環依賴,盡早持有對象的引用
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
//...
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
//提前曝光執行個體化完成暫未進行屬性注入的bean對象,以解決屬性注入的循環引用問題
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
可以看到在 createBeanInstance 方法執行個體化對象之後(此時還未進行依賴注入)就會暴露到 singletonFactories 三級緩存集合中,以解決屬性注入的循環引用問題。
在 AbstractBeanFactory 的 doGetBean 方法中,調用 createBean 方法
//建立單例模式的Bean的執行個體對象
if (mbd.isSingleton()) {
//調用匿名内部類建立Bean執行個體對象,建立Bean執行個體對象,并且注冊給所依賴的對象
sharedInstance = getSingleton(beanName, () -> {
try {
//建立一個指定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緩存中清除執行個體對象
destroySingleton(beanName);
throw ex;
}
});
//擷取給定Bean的執行個體對象,主要完成FactoryBean擷取執行個體化對象過程
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
繼續跟進 DefaultSingletonBeanRegistry.getSingleton() 方法,源碼如下:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
//全局變量需要同步
synchronized (this.singletonObjects) {
//首先檢查對應的bean是否已經加載過,因為singleton模式其實就是複用已建立的bean
Object singletonObject = this.singletonObjects.get(beanName);
//如果為空才可以進行singleton的bean的初始化
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
//初始化bean
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
//加入代表已經初始化完成的單例bean緩存中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
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);
}
}
從上面可看到,當 Bean 執行個體初始化完成後将會删除 singletonFactories 和 earlySingletonObjects 緩存中的引用,添加到 singletonObjects 緩存中。
總結
最後梳理一下Spring解決循環依賴的流程
1. 類 A 與 B 中屬性互相引用,造成循環依賴
2. A 執行個體化完成,将自己提前曝光到 singletonFactories 緩存中
3. A 執行個體進行依賴注入,發現自己依賴對象 B,就嘗試擷取 B 執行個體引用
4. B 此時還沒初始化,先進行執行個體化并将自己曝光到 singletonFactories 緩存中
5. B 執行個體進行依賴注入,發現自己依賴對象 A,就嘗試擷取 A 執行個體引用
6. 由于 A 執行個體尚未初始化完成,從 singletonObjects 中擷取不到,從 earlySingletonObjects 中擷取也沒有,最後從 singletonFactories 中擷取到
7. A 執行個體從 singletonFactories 中删除,添加到 earlySingletonObjects 緩存中去
8. B 執行個體拿到 A 執行個體引用後順利完成依賴注入及初始化,并将自己從 singletonFactories 緩存中删除,添加到 singletonObjects 緩存中
9. A 執行個體擷取 B 執行個體引用後,A 執行個體也能繼續完成依賴注入及後續初始化操作
10. A 是從 earlySingletonObjects 中删除,添加到 singletonObjects 緩存中
在整個過程中,A 和 B 的執行個體引用并未改變過。隻是在 Bean 生命周期的不同階段。