Spring IOC(七)Spring 中循環依賴的解決

首先拋出結論,Spring 目前無法解決 構造方法循環依賴,而可以解決字段的循環依賴。
例如以下兩種情況:

  1. 字段依賴
    CyclicA:
@Component
public class CyclicA {
    @Autowired
    private CyclicB cyclicB;
}

CyclicB

@Component
public class CyclicB {
    @Autowired
    private CyclicA cyclicA;
}
  1. 構造方法依賴
    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 源代碼。

字段注入

從前面幾篇文章中,瞭解到,SpringgetBean方法 主體流程爲 實例化 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;
	}
  1. 首先會嘗試從 singletonObjects 中獲取
  2. 而後如果beanName正在創建中 isSingletonCurrentlyInCreation ,則會從 earlySingletonObjects 中 獲取。
  3. 如果 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 則是將信息放入到 singletonFactoriesregisteredSingletons 中。

	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是:

  1. A首先完成實例化第一步,並且將自己提前曝光在 singletonFactoriesregisteredSingletons 中。
  2. 而後,A開始執行 popularBean方法,在 對 @Autowired 字段進行解析時,會getBean獲取B,此時如果B沒有創建,就會去創建 B。
  3. 創建B的過程中,由於B 中有對 A@Autowired,此時就會去getBean獲取A。
  4. 此時嘗試 singletonObjects沒有,而嘗試 earlySingletonObjects 中獲取沒有,最後在 singletonFactories 中獲取到了。所以此時B拿到了A,B完成了初始化。
  5. 而後A從 singletonObjects 中獲取到了B, 所以最終A結束了getBean獲取B的操作。
  6. 至此,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 會同時有 CyclicCCyclicD

非單例循環依賴

對於“prototype”作用域bean, Spring 容器無法完成依賴注入,因爲Spring 容器不進行緩 存“prototype”作用域的bean ,因此無法提前暴露一個創建中的bean 。
但是可以使用 @LookUp在單例中使用非單例(prototype) 實例。

覺得博主寫的有用,不妨關注博主公衆號: 六點A君。
哈哈哈,一起研究Spring:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章