【來自拉鈎java高薪訓練營學習筆記】
1. 什么是循環依賴
循環依賴其實就是循環引⽤,也就是兩個或者兩個以上的 Bean 互相持有對⽅,最終形成閉環。⽐如A依賴於B,B依賴於C,C⼜依賴於A
注意:
這⾥不是函數的循環調⽤,是對象的相互依賴關系。
循環調⽤其實就是⼀個死循環,除⾮有終結 條件。
Spring中循環依賴場景有:
- 構造器的循環依賴(構造器注⼊)
- Field 屬性的循環依賴(set注⼊)
其中,構造器的循環依賴問題⽆法解決,只能拋出 BeanCurrentlyInCreationException 異常,在解決屬性循環依賴時,spring采⽤的是提前暴露對象的⽅法。
2. 循環依賴處理機制
- 單例 bean 構造器參數循環依賴(⽆法解決)
- prototype 原型 bean循環依賴(⽆法解決)
因為prototype 原型 bean ,產生對象之后是不在容器中管理的。 - 單例bean通過setXxx或者@Autowired進行循環依賴(可以解決)
2.1 演示場景:
//lagouBen 依賴於 ItBean
public class LagouBean {
private ItBean itBean;
public void setItBean(ItBean itBean) {
this.itBean = itBean;
}
public LagouBean() {
System.out.println("LagouBean 構造器");
}
}
//ItBean 依賴於 LagouBen
public class ItBean {
private LagouBean lagouBean;
public void setLagouBean(LagouBean lagouBean) {
this.lagouBean = lagouBean;
}
public ItBean() {
System.out.println("ItBean...構造器");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="lagouBean" class="com.lagou.edu.LagouBean" >
<property name="ItBean" ref="itBean>"></property>
</bean>
<bean id="itBean" class="com.lagou.edu.ItBean" >
<property name="LagouBean" ref="lagouBean>"></property>
</bean>
</beans>
2.2 處理機制簡圖
總結:
A依賴於B ,B 依賴於A
A在創建過程中 :
-
首先會創建Bean實例(僅僅調用構造方法,但是尚未設置屬性,通過反射完成對象的初始化),
-
然后判斷是否是單例,是否有循環依賴。
-
把創建好的Bean實例放入三級緩存——singletonFactories
-
然后將要給A Bean裝配屬性,發現依賴B
-
調用deGetBean() 想拿到B,首先從一級緩存(singletonObjects)中拿,然后從二級緩存中(earlySingletonObjects)拿,然后從三級緩存(singletonFactories)拿,都拿不到,那就開始創建B Bean
-
把創建好的B Bean實例放入三級緩存——singletonFactories,發現依賴於A
-
調用deGetBean() 想拿到A,首先從一級緩存(singletonObjects)中拿,然后從二級緩存中(earlySingletonObjects)拿,都沒拿到。然后從三級緩存中拿,拿到了
-
拿到A Bean之后如上圖,放到二級緩存(earlySingletonObjects)中,然后從三級緩存(singletonFactories)中刪除。然后給B bean賦值了。
-
此時B Bean 就裝配好了 放入一級緩存池中。
-
B 裝配好了之后,A 就能順利的裝配了,然后調用
addSingleton()
方法,把A 從二級三級緩存中刪除,然后放到一級緩存也就是單例池中。
-
完成
注意:
這個案例中,B不會放到二級緩存,只有在B依賴的一個對象尚未實例化的時候才會把B放到二級緩存。例如:
A依賴B,B依賴A和C,C依賴B。 先創建A,把尚未賦值的A放到三級緩存,然后賦值B,找不到B,然后創建B,然后把尚未賦值的B放到三級緩存,然后在創建B的過程中從三級緩存找A(同時把A從三級緩存中刪除然后加入到二級緩存),然后B還有個屬性C,賦值C,從緩存中找不到C,然后創建C,然后把尚未賦值的B放到三級緩存,創建C的過程中發現C依賴於B,然后可以從三級緩存中找到B,然后把B放到二級緩存,C就裝配完畢了,放到一級緩存。同時B也有了A和C,B裝配完畢了,放到一級緩存。 A依賴B,B已經OK了,那么A也裝配完畢了。
/** Cache of singleton factories: bean name to ObjectFactory. */
//三級緩存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
//二級緩存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/** Cache of singleton objects: bean name to bean instance. */
//一級緩存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);