所謂的循環依賴,是指在實例化A類時,注入B類,實例化B類時,注入A類。
依賴注入分為構造器注入和字段注入,下面依次對這兩種情況分析。
構造器注入時
@Configuration
@RequiredArgsConstructor
public class Adam {
private final Eve eve;
}
複製代碼
@RequiredArgsConstructor
@Configuration
public class Eve {
private final Adam adam;
}
複製代碼
AbstractAutowireCapableBeanFactory->createBeanInstance():
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
複製代碼
在選擇構造器注入的時候,會遍歷字段,根據beanClass反推beanName,然後實例化。 一路debug,最終找到了拋出異常的調用棧:
DefaultSingletonBeanRegistry->getSingleton():
beforeSingletonCreation(beanName);
DefaultSingletonBeanRegistry->beforeSingletonCreation():
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
複製代碼
與之對應的是afterSingletonCreation方法
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
複製代碼
結論
singletonsCurrentlyInCreation是一個Set集合,表示當前正在創建的bean名稱,創建完成後,會移除。
原來,當同一個bean未完成實例化,又再一次實例化時,singletonsCurrentlyInCreation.add(beanName)返回false,進而拋出了異常,導致程序報錯
字段注入時
@Component
public class Adam {
@Autowired
private eve;
}
複製代碼
@Component
public class Eve {
@Autowired
private Adam adam;
}
複製代碼
這樣寫發現並沒有報錯,一路debug,發現是這段代碼起的作用:
AbstractBeanFactory->doGetBean():
Object sharedInstance = getSingleton(beanName);
AbstractBeanFactory->getSingleton():
return getSingleton(beanName, true);
AbstractBeanFactory->getSingleton():
// 此時singletonObjects未完成,所以肯定是null
Object singletonObject = this.singletonObjects.get(beanName);
// 根據前面的分析,singletonsCurrentlyInCreation肯定包含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);
}
}
}
}
複製代碼
此時發現,earlySingletonObjects.get(beanName)
為空,singletonFactories.get(beanName)
獲取到了值,繼而獲取到了singletonObject(這裏看出earlySingletonObjects創建,對應的singletonFactory會移除)。重新debug,發現是這裏給singletonFactories賦了值:
AbstractAutowireCapableBeanFactory->doCreateBean():
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
DefaultSingletonBeanRegistry->addSingletonFactory():
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
複製代碼
這段代碼是在實例化完成之後進行的,也就是説,當構造器注入的時候,並沒有添加到singletonFactories,所以getSingleton(beanName)
返回空,進行了第二次的實例化,從而導致報錯(這裏看出singletonFactories創建,對應的earlySingletonObjects會移除)。繼續debug,最終singletonFactories和earlySingletonObjects都被清除了:
DefaultSingletonBeanRegistry->getSingleton():
addSingleton(beanName, singletonObject);
DefaultSingletonBeanRegistry->addSingleton():
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
複製代碼
當singletonObjects創建的時候,earlySingletonObjects和singletonFactories的使命就完成了~
結論
對於字段注入,IoC容器使用了singletonFactories和earlySingletonObjects來預防循環依賴。