Spring循環(huán)依賴詳解(看這篇就夠了)
循環(huán)依賴
在探討Spring循環(huán)依賴的解決方式以前,我們先來回憶一下什么是循環(huán)依賴。
循環(huán)依賴:就是多個bean之間相互依賴,形成了一個閉環(huán)。
比如:A依賴于B、B依賴于A,如下圖所示:
圖片
體現(xiàn)到代碼中為:
@Component
public class A{
// 依賴B
@Autowired
private B b;
public B getB() {
return b;
}
}
@Component
public class B {
// 依賴A
@Autowired
private A a;
public A getA() {
return a;
}
}
Spring的循環(huán)依賴過程:
- 首先實例化A -> 屬性填充注入B -> B還沒有實例化;
- 需要先進行實例化B(A等待) -> 實例化B -> 注入A -> A實例化未完成,無法注入 -> 實例化B失敗 -> 實例化A失??;
這樣反復就進入了死循環(huán)了。
Spring如何解決循環(huán)依賴
下面我還是用A -> B -> A的場景,我們按照過程一步步來分析,看一下Spring是如何解決循環(huán)依賴的。
第一步:首先是實例化A
圖片
第二步:屬性注入B
執(zhí)行到屬性填充環(huán)節(jié)需要注入B,因為Spring管理的bean默認是單例的,為防止重復創(chuàng)建Spring會先去容器中查找B,如果查找不到再進行創(chuàng)建。
如果Spring容器中是沒有B,需要先實例化B,流程和實例化一致,如下圖所示:
圖片
第三步:屬性注入A
此時B也執(zhí)行到屬性填充的環(huán)節(jié)了,此時又需要注入A,此時還是會先去Spring容器中查找A,此時的A雖然沒在單例池中,但是因為在創(chuàng)建中并且也在三級緩存中了。
所以此時獲取A的流程就發(fā)生了變化,不再是直接創(chuàng)建,而是會從三級緩存中獲取A,如下圖所示:
圖片
三級緩存存放的并不是bean對象,而是生成bean的ObjectFactory,然后放入二級緩存中,同時返回A進行依賴注入。
第四步:初始化B
此時,繼續(xù)執(zhí)行B的實例化, 并將B從正在創(chuàng)建列表移出 , 將B放入一級緩存,同時將B在二級緩存和三級緩存中刪,最后返回B。
圖片
在B實例化完成并返回后,A的實例化流程也從等待著蘇醒繼續(xù)執(zhí)行,后續(xù)流程和B的完全一致。
圖片
然后整個流程:A -> B -> A的場景就結(jié)束了。
這樣Spring通過三級緩存來解決循環(huán)依賴的,提前暴露的對象存放在三級緩存中,二級緩存存放過渡bean,一級緩存存放最終形態(tài)的bean。
Spring三級緩存
// 從上至下 分表代表這“三級緩存”
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一級緩存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二級緩存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三級緩存
1.三級緩存(singletonFactories)
singletonFactories:單例對象工廠的cache,存放 bean 工廠對象,用于解決循環(huán)依賴。
2.二級緩存(earlySingletonObjects)
主要存放過渡bean,也就是三級緩存中ObjectFactory產(chǎn)生的對象。
提前曝光的單例對象的cache,存放原始的 bean 對象:尚未填充屬性,用于解決循環(huán)依賴。
3.一級緩存(singletonObjects)
也被稱為單例池,去存放已經(jīng)創(chuàng)建完成,并且屬性也注入完畢的對象,一般情況我們獲取bean都是從這里獲取的。