Spring如何解決循環依賴

一、什麼是循環依賴

  多個bean之間相互依賴,形成了一個閉環。 比如:A依賴於B、B依賴於c、c依賴於A

  通常來說,如果問spring容器內部如何解決循環依賴, 一定是指默認的單例Bean中,屬性互相引用的場景。也就是說,Spring的循環依賴,是Spring容器注入時候出現的問題。

    

二、Spring如何解決循環依賴

1,Spring中單例Bean的三級緩存

  

  • 第一級緩存〈也叫單例池)singletonObjects:存放已經經歷了完整生命周期的Bean對象
  • 第二級緩存: earlySingletonObjects,存放早期暴露出來的Bean對象,Bean的生命周期未結束(屬性還未填充完整)
  • 第三級緩存: Map<String, ObiectFactory<?>> singletonFactories,存放可以生成Bean的工廠

2,Spring中Bean的生命周期

      

     

3,Bean初始化主要方法

  

  • getSingleton:希望從容器裏面獲得單例的bean,沒有的話
  • doCreateBean: 沒有就創建bean
  • populateBean: 創建完了以後,要填充屬性
  • addSingleton: 填充完了以後,再添加到容器進行使用

4,具體說明

  • A創建過程中需要B,於是A將自己放到三級緩存裏面,去實例化B
  • B實例化的時候發現需要A,於是B先查一級緩存,沒有,再查二級緩存,還是沒有,再查三級緩存,找到了A然後把三級緩存裏面的這個A放到二級緩存裏面,並刪除三級緩存裏面的A
  • B順利初始化完畢,將自己放到一級緩存裏面(此時B裏面的A依然是創建中狀態)然後回來接着創建A,此時B已經創建結束,直接從一級緩存裏面拿到B,然後完成創建,並將A放到一級緩存中。

5,圖解

  

三、為什麼使用三級緩存

1,使用一級緩存

  實例化A -> 將半成品的A放入singletonObjects中->填充A的屬性時發現取不到B->實例化B->從singletonObjects中取出A填充B的屬性->將成品B放入singletonObjects->將B填充到A的屬性中->將成品A放入singletonObjects。

  問題:這種基本流程是通的,但是如果在整個流程進行中,有另一個線程要來取A,那麼有可能拿到的只是一個屬性都為null的半成品A,這樣就會有問題。

2,使用二級緩存

a)使用singletonObjects和earlySingletonObjects

  成品放在singletonObjects中,半成品放在earlySingletonObjects中

  流程可以這樣走:實例化A ->將半成品的A放入earlySingletonObjects中 ->填充A的屬性時發現取不到B->實例化B->將半成品的A放入earlySingletonObjects中->從earlySingletonObjects中取出A填充B的屬性->將成品B放入singletonObjects,並從earlySingletonObjects中刪除B->將B填充到A的屬性中->將成品A放入singletonObjects並刪除earlySingletonObjects。

  問題:這樣的流程是線程安全的,不過如果A上加個切面(AOP),這種做法就沒法滿足需求了,因為earlySingletonObjects中存放的都是原始對象,而我們需要注入的其實是A的代理對象

b)使用singletonObjects和singletonFactories

  成品放在singletonObjects中,半成品通過singletonFactories來獲取

  流程是這樣的:實例化A ->創建A的對象工廠並放入singletonFactories中 ->填充A的屬性時發現取不到B->實例化B->創建B的對象工廠並放入singletonFactories中->從singletonFactories中獲取A的對象工廠並獲取A填充到B中->將成品B放入singletonObjects,並從singletonFactories中刪除B的對象工廠->將B填充到A的屬性中->將成品A放入singletonObjects並刪除A的對象工廠。

  問題:這樣的流程也適用於普通的IOC以及有並發的場景,但如果A上加個切面(AOP)的話,這種情況也無法滿足需求