Spring是如何解決循環依賴的

在spring中我們有可能會遇到這種情況,A依賴B,B又依賴A,正常情況下,我們用@Reference或者@Autowired註解,是不會有問題的,可在我們用構造方法的時候,就會出現問題:

    public AssistantDoctorController(UserDoctorController userDoctorController) {
        this.userDoctorController = userDoctorController;
    }
    public UserDoctorController(AssistantDoctorController assistantDoctorController) {
        this.assistantDoctorController = assistantDoctorController;
    }

創建請求bean
報錯的意思是被請求的bean正在被創建,是否存在循環依賴,那spring是如何判斷循環依賴,又是如何解決循環依賴的呢,網絡上很多文章說的有些不是很清楚,所以想寫一篇博文,說的儘量清晰些。

一、爲什麼構造方法的循環依賴會報錯

首先看下AbstractBeanFactoryBeanFactory的結構圖:
AbstractBeanFactory結構圖
可以發現AbstrapctBeanFactory繼承了DefaultSingletonBeanRegistry類,這個類是註冊bean使用的,可見註冊bean也是在AbstractBeanFactory中完成,在註冊bean之前會調用它的beforeSingletonCreation方法,如下:

	protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

這個方法中會檢查singletonsCurrentlyInCreation屬性是否存在這個bean,沒有就放入,有則會拋出異常,等到創建bean完成,再調用afterSingletonCreation方法將已經創建的bean刪除。

  • singletonsCurrentlyInCreation:記錄正在創建中的bean。
    我舉例說明:
    A->B->A
  1. 在創建A的時候,A的beanName會被加入到singletonsCurrentlyInCreation中
  2. 因爲A依賴了B,此時循環創建B,然後將B放入到singletonsCurrentlyInCreation中,
  3. 此時B又依賴了A,然後再將A放入到singletonsCurrentlyInCreation中,此時因爲有A在singletonsCurrentlyInCreation中,所以放入失敗,就會拋出異常。

爲什麼用註解循環依賴不會報錯

因爲在創建bean的時候,會調用bean的構造方法去實例化bean,並緩存到beanFactory中,而用註解做的構造方法中,不需要在構造方法中注入bean,所以不會報錯。
總結就是:構造方法中的bean是在創建bean的時候注入的,而註解的bean是後面注入的,所以報錯是因爲構造方法的緣故。
那麼問題又來了,註解注入的bean是在什麼時間注入的呢?

註解注入Bean的時機

真正的執行注入,是在當前bean的依賴bean創建完成之後的,AbstractAutowireCapableBeanFactory類中的doCreateBean方法中的populateBean方法中完成的。
代碼如下:

for (BeanPostProcessor bp : getBeanPostProcessors()) {
	if (bp instanceof InstantiationAwareBeanPostProcessor) {
		InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
		pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
		if (pvs == null) {
			return;
		}
	}
}

其中注入@Resource註解,使用的是CommonAnnotationBeanPostProcessor,注入@AutoWired註解使用的是AutowiredAnnotationBeanPostProcessor,執行順序是限制性CommonAnnotationBeanPostProcessor,再執行AutowiredAnnotationBeanPostProcessor,所以注入的時候會先根據名稱注入,然後根據類型進行注入。

循環依賴的解決

那麼注入了Bean之前,如果有循環依賴如何解決呢,我們要先明白幾個屬性,幾個DefaultSingletonBeanRegistry的屬性,而我認爲理解循環依賴的關鍵就是弄懂這幾個屬性,以及他們何時進行了操作

  • singletonFactories:bean的創建方法的緩存。
	/** Cache of singleton factories: bean name --> ObjectFactory */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

這個值是什麼時候設置進去的呢?是在doCreateBean方法中,創建了BeanInstance之後,代碼如下:

addSingletonFactory(beanName, new ObjectFactory<Object>() {
	@Override
	public Object getObject() throws BeansException {
		return getEarlyBeanReference(beanName, mbd, bean);
	}
});

這個ObjectFactory的getObject()中有三個入參:

  1. beanName
  2. mbd:作用不大
  3. bean:此時創建的bean是通過createBeanInstance方法創建的,還沒有進行populateBean方法,所以現在的an是沒有任何屬性注入的,用構造方法創建出來的一個Bean,而getObject返回的就是這樣的一個早期的Bean,所以叫EarlyBean。
  4. 這個beanName對應的ObjecFactory會在EarlyBean添加到earlySingletonObjects屬性中後進行刪除,代碼如下:

singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
  • registeredSingletons:DefaultSingletonBeanRegistry的這個屬性是按照依賴的順序放入了beanName,在spring的使用中無用,主要是給springMoce或者其他方面,可以不用關注。
  • earlySingletonObjects:這個屬性的key爲beanName,value是我們上面說的earlyBean。bean放入到earlySingletonObjects的時機是在設置注入屬性的時候,就是在:
  1. CommonAnnotationBeanPostProcessor設置@Resource屬性時候或者
  2. AutowiredAnnotationBeanPostProcessor設置@Autowired屬性的時候
    通過beanName調用DefaultSingletonBeanRegistry的getSingleton方法的時候進行的插入,以及singletonFactories中屬性的刪除
  • singletonObjects:這個屬性中緩存了beanName和對應的已經注入好的Bean
	/** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

這個裏面的Bean會在Bean中的注入屬性已經完成注入,成爲一個完整正常的bean之後放入的。它是在AbstractBeanFactory的doGetBean方法中的DefaultSingletonBeanRegistry的addSingleton方法中完成的。

也有人把Bean的循環依賴注入成爲三級緩存完成那麼

  • 第一層緩存:singletonFactories
  • 第二層緩存:earlySingletonObjects
  • 第三層緩存:singletonObjects

總結:這就是Bean的解決循環依賴的方法與過程,正常的Bean,即使沒有循環依賴也是這個處理邏輯

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