前文
在寫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放到三級緩存中
③從三級緩存中取出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