Spring如何使用三級快取解決迴圈依賴
首先來了解一下什麼是迴圈依賴
@Component
public class A {
@Autowired
B b;
}
@Component
public class B {
@Autowired
A a;
}
在物件A建立過程中,需要注入B,因為容器中沒有B,則去建立B,B建立過程中又需要注入A,而A在等待B的建立,B在等待A的建立,導致兩者都無法建立成功,無法加入到單例池供使用者使用。
Spring則通過三級快取來解決迴圈依賴的問題,另外如果物件的作用範圍是Prototype,則無法通過三級快取解決迴圈依賴,會丟擲BeanCurrentlyInCreationException
異常,構造注入的方式,也無法解決迴圈依賴,只有set注入可以解決。
那麼三級快取又是什麼呢?
三級快取就是三個Map
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
//一級快取(單例池,經過完成生命週期的物件會放入其中)
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);
Spring Bean物件從建立到初始化大致會經過四個流程
getSingleton()
、doCreateBean()
、populateBean()
、addSingleton()
-
getSingleton
:從單例池中獲取bean物件,如果沒有,則進行建立 -
doCreateBean()
:建立bean物件 -
populateBean()
:填充依賴,如果被填充的物件不存在於單例池,則進行建立等四個流程 -
addSingleton()
:將初始化完成的物件加入到單例池
迴圈依賴的物件在三級快取中的遷移過程
-
A 建立過程中需要 B, 於是 A 將自己放到三級快取裡面,去例項化 B
-
B 例項化的時候發現需要 A,於是 B 先查一級快取,沒有,再查二級快取,還是沒有,再查三級快取
找到了A,然後把三級快取中的 A 放到二級快取,並刪除三級快取中的 A
-
B 順利初始化完畢,將自己放到一級快取中(此時 B 中的 A 還是建立中狀態,並沒有完全初始化),刪除三級快取中的 B
然後接著回來建立 A,此時 B 已經完成建立,直接從一級快取中拿到 B,完成 A 的建立,並將 A 新增到單例池,刪除二級快取中的 A
圖示: