Spring中怎麼解決循環依賴?

前文

在寫Spring之getBean
的時候提到過在這個過程中要解決循環依賴。

什麼是循環依賴?

A類依賴B類,B類依賴A類。 這就是循環依賴。
如下就是一段在Spring中會造成循環依賴的代碼

@Component
public class A {
    private B b;

    @Autowired
    public A(B b) {
        this.b = b;
    }
}
@Component
public class B {
    private A a;
    @Autowired
    public B(A a) {
        this.a = a;
    }
}

啓動後會報錯:error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

哪兒在拋錯?

跟蹤源碼查看一下爲何會報錯。

Spring之getBean
知道了啓動的時候會去創建所有非lazy的單實例bean

主要跟蹤getSingleton:
1.沒有已經加載好的實例,那麼準備創建了。
2.創建前先使用beforeSingletonCreation(beanName);去檢查是否【正在創建】,拋出循環依賴的異常。

	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
		//關注:從【一級緩存】中拿【已經加載好的】bean實例
			Object singletonObject = this.singletonObjects.get(beanName);
			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 {
					//關注:創建實例過程--調用createBean
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					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) {
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

但是爲何會拋錯呢?

debug跟一下。 發現在第getBean(“a”)的時候,會有如下步驟:
1.getBean(“a”)。
2.標記A【正在創建】。
3.得到A的構造函數,嘗試去創建A的實例。
4.在創建A的實例之前,要得到構造函數的參數B的實例。
5.getBean(“b”)。
6.標記B【正在創建】。
7.重複步驟2-3. 不過此時是得到B的構造函數,並嘗試獲取A的實例。
8.getBean(“a”) 。ps:注意,這裏是第二次getBean(“a”)了。
9.再次標記A【正在創建】中。—標記失敗,拋出異常。

具體過程參考下圖:
ps:網圖改了一下
在這裏插入圖片描述
由此,知道爲何構造注入會引起循環依賴的時候拋錯。
如果對這個注入過程不熟悉的,可以參考我之前的Spring之getBean

那麼如何避免拋錯呢?

1.允許循環依賴(默認是允許的)。①
2.使用非構造注入,如@Autowired寫在成員變量或者setter方法上。
3.要有無參構造函數,或者是不包含被依賴的成員變量的構造函數(避免在一開始注入的時候就需要一個擁有完整成員變量的B)。

例如之前的例子:

開始A實例的創建
1.getBean(“a”)->標記A【正在創建中】。
2.創建A的實例的時候,用無參構造創建一個A的實例a。
3.將半成品(無成員變量)a放到三級緩存中。②
4.populate的時候,嘗試注入成員變量b。


開始B的實例創建

  5.getBean(“b”)創建一個B的實例>標記B【正在創建中】
  6.B通過createBeanInstance創建實例後,
  7.將半成品(無成員變量)b放到三級緩存中。
  8. populate去注入成員變量a.
  9.getBean(“a”)->getSingleton(“a”, true)->從一級緩存(singletonObjects)中取->取不到則從二級緩存(earlySingletonObjects)中取->取不到則從三級緩存(singletonFactories)中取->從三級緩存中取出ObjectFactory得到A的實例a,將a放入二級緩存,並將ObjectFactory從三級緩存中移出。③
  10.得到了a的實例(半成品),注入到b中。繼續走完B創建。
b實例的創建完成


繼續A實例的創建
11.將完成品B的實例b,注入到A中。 繼續走完A創建。


非構造注入整個流程
ps:偷來的圖
在這裏插入圖片描述

①不允許循環依賴當然要報錯
在這裏插入圖片描述

②將半成品(無成員變量)a放到三級緩存中

將半成品(無成員變量)a放到三級緩存中在這裏插入圖片描述

③從三級緩存中取出ObjectFactory得到A的實例a,將a放入二級緩存,並將ObjectFactory從三級緩存中移出

在這裏插入圖片描述

爲何是三級緩存?

個人認爲,第三級緩存的作用, 是留了一個鉤子,方便【獲取半成品實例】的時候做一些定製。
如下源碼:實現SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference可以完成該定製。

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

參考資料

https://blog.csdn.net/f641385712/article/details/92801300
https://blog.csdn.net/weixin_42228338/article/details/97163101

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