首先拋出結論,Spring 目前無法解決 構造方法循環依賴,而可以解決字段的循環依賴。
例如以下兩種情況:
- 字段依賴
CyclicA:
@Component
public class CyclicA {
@Autowired
private CyclicB cyclicB;
}
CyclicB
@Component
public class CyclicB {
@Autowired
private CyclicA cyclicA;
}
- 構造方法依賴
CyclicC
@Component
public class CyclicC {
private CyclicD cyclicD;
@Autowired
public CyclicC( CyclicD cyclicD) {
this.cyclicD = cyclicD;
}
}
CyclicD
public class CyclicC {
private CyclicD cyclicD;
@Autowired
public CyclicC( CyclicD cyclicD) {
this.cyclicD = cyclicD;
}
}
對於上面兩個例子,1可以成功注入,而2則無法進行注入。
下面以上面兩種情況來具體分析Spring 源代碼。
字段注入
從前面幾篇文章中,瞭解到,Spring
的 getBean
方法 主體流程爲 實例化 Bean和 初始化 Bean 實例(包括初始化字段和 調用init方法)。
而 實例化Bean則是調用其構造方法進行調用,而初始化則是對 @Autowired
和 @Value
bean 進行賦值等操作,其原來也是對依賴進行進一步 getBean
調用。所以並不會報錯。
從前面文章 分析 在getBean時候,在getSingleton時:
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;
}
- 首先會嘗試從
singletonObjects
中獲取 - 而後如果beanName正在創建中
isSingletonCurrentlyInCreation
,則會從earlySingletonObjects
中 獲取。 - 如果
earlySingletonObjects
再一次爲null,而 允許獲取,則會從singletonFactories
中獲取構造工廠,如果獲取到了則利用其去獲取構造方法。而從前面分析,singletonFactories
是的是一個lambda
表達式。
另一方面,在每一個bean實例化時候,都會進入 doCreateBean
,並且在 實例化後(populateBean前)將類信息放入緩存中:
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
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));
}
而 addSingletonFactory
則是將信息放入到 singletonFactories
、registeredSingletons
中。
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);
}
}
}
所以當A依賴B,而B又依賴A是:
- A首先完成實例化第一步,並且將自己提前曝光在
singletonFactories
和registeredSingletons
中。 - 而後,A開始執行 popularBean方法,在 對
@Autowired
字段進行解析時,會getBean
獲取B,此時如果B沒有創建,就會去創建 B。 - 創建B的過程中,由於B 中有對 A
@Autowired
,此時就會去getBean
獲取A。 - 此時嘗試
singletonObjects
沒有,而嘗試earlySingletonObjects
中獲取沒有,最後在singletonFactories
中獲取到了。所以此時B拿到了A,B完成了初始化。 - 而後A從
singletonObjects
中獲取到了B, 所以最終A結束了getBean獲取B的操作。 - 至此,A拿到了B,B拿到了A,都完成了初始化。
構造方法注入
在getBean時候,從getBean->doGetBean->getSingleton->beforeSingletonCreation,在 beforeSingletonCreation
會有一次檢查,檢查 當前bean是否在創建中,如果 在創建bean時,發現自己已經在當前創建的bean池中,那麼就會報錯:
構造器注入和字段注入不一樣,beforeSingletonCreation
執行比 實例化字段早,即在實例化之前,先檢查自己是否在創建中:
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
最終singletonsCurrentlyInCreation
會同時有 CyclicC
和 CyclicD
。
非單例循環依賴
對於“prototype”作用域bean, Spring 容器無法完成依賴注入,因爲Spring 容器不進行緩 存“prototype”作用域的bean ,因此無法提前暴露一個創建中的bean 。
但是可以使用 @LookUp
在單例中使用非單例(prototype) 實例。
覺得博主寫的有用,不妨關注博主公衆號: 六點A君。
哈哈哈,一起研究Spring: