日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

前言

相信很多小伙伴在工作中都會遇到循環依賴,不過大多數它是這樣顯示的:

Spring 是如何解決循環依賴的?

 

還會提示這么一句:

Requested bean is currently in creation: Is there an unresolvable circular reference?

老鐵!這就是發生循環依賴了!

當然這里是一個異常情況。

在我的一篇文章中介紹如何避免 Spring 自調用事務失效,其中網友給建議,說可以在類中注入自身,然后調用,而注入自身的過程也是循環依賴的處理過程。

下面就一起看一看,什么是循環依賴,以及 Spring 是如何解決循環依賴的?

什么是循環依賴

Spring 是如何解決循環依賴的?

 

Circular dependencies

Dependency Resolution Process[1]

Spring IoC 容器會在運行時檢測到構造函數注入循環引用,并拋出
BeanCurrentlyInCreationException。

所以要避免構造函數注入,可以使用 setter 注入替代。

根據官方文檔說明,Spring 會自動解決基于 setter 注入的循環依賴。

當然在咱們工作中現在都使用 @Autowired 注解來注入屬性。

PS: @Autowired 是通過反射進行賦值。

這里從我們最經常使用的場景切入,看 Spring 是如何解決循環依賴的?

代碼

@Service
public class CircularServiceA {

    @Autowired
    private CircularServiceB circularServiceB;
}

@Service
public class CircularServiceB {

    @Autowired
    private CircularServiceC circularServiceC;
}

@Service
public class CircularServiceC {

    @Autowired
    private CircularServiceA circularServiceA;

}

這里有 A、B、C 三個類,可以看到發生了循環依賴:

Spring 是如何解決循環依賴的?

 

循環依賴

但是即使發生了循環依賴,我們依然可以啟動 OK,使用并沒有任何影響。

Spring 是如何解決循環依賴的

在 Spring 單例 Bean 的創建 中介紹介紹了使用三級緩存。

singletonObjects: 一級緩存,存儲單例對象,Bean 已經實例化,初始化完成。

earlySingletonObjects: 二級緩存,存儲 singletonObject,這個 Bean 實例化了,還沒有初始化。

singletonFactories: 三級緩存,存儲 singletonFactory。

Spring 是如何解決循環依賴的?

 

當然,這里看著比較長,可以簡化一下:

Spring 是如何解決循環依賴的?

 

通過 Debug 來說明生成過程

從 preInstantiateSingletons 方法開始:

添加斷點 beanName.equals("circularServiceA")

啟動Debug:

Spring 是如何解決循環依賴的?

 

Start

會從緩存中獲取單例 Bean

Spring 是如何解決循環依賴的?

 

這里很顯然獲取不到,繼續執行,創建單例實例

Spring 是如何解決循環依賴的?

 

發現是單例再次獲取

Spring 是如何解決循環依賴的?

 

這里還會從一級緩存獲取一次 circularServiceA , 沒有獲取到,將 circularServiceA 添加到在創建的池子里面 (
singletonsCurrentlyInCreation 是一個 set 集合)。

然后會調用工廠方法 createBean(beanName, mbd, args) 創建對象。

Spring 是如何解決循環依賴的?

 

createBean 方法

在 createBean 中去實例化 Bean 。

Spring 是如何解決循環依賴的?

 

判斷是否是循環引用,是的話需要添加到三級緩存中。

Spring 是如何解決循環依賴的?

 

添加到三級緩存

circularServiceA 不在一級緩存中,則將 circularServiceA 的 singletonFactory 添加到 三級緩存 (singletonFactories) 中,同時從二級緩存中移除。

到這一步為止,circularServiceA 已經在三級緩存中了。

開始對 Bean 的屬性進行賦值。

Spring 是如何解決循環依賴的?

 

屬性賦值

在 populateBean 方法中執行到

PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrAppedInstance(), beanName);

就會對屬性進行賦值

Spring 是如何解決循環依賴的?

 

屬性賦值

在 injet 方法中,回去解決相關依賴。

Spring 是如何解決循環依賴的?

 

解決依賴

繼續 Debug ,發現解決依賴,最后發現其實又調用回 beanFactory.getBean(beanName);

不過這次創建的是 circularServiceB。

Spring 是如何解決循環依賴的?

 

下面是調用鏈:

Spring 是如何解決循環依賴的?

 

調用鏈

circularServiceB 的過程和 circularServiceA 的一樣,也是創建了三級緩存,然后去創建 circularServiceC

Spring 是如何解決循環依賴的?

 

singletionFactories

這時候三級緩存里面有它們三個的 singletonFactory 。

Spring 是如何解決循環依賴的?

 

circularServiceC 也調用到 doGetBean 方法去獲取 circularServiceA

不過這次 調用到 Object sharedInstance = getSingleton(beanName); 的時候, circularServiceA 已經存在了。

Spring 是如何解決循環依賴的?

 

這次調用雖然沒有從一級緩存 (singletonObjects) 中獲取到 circularServiceA,但是 circularServiceA 在創建中,所以進入判斷

在這里執行完之后, circularServiceA 從三級緩存升級到二級緩存

Spring 是如何解決循環依賴的?

 

使用反射對 circularServiceC 中的 circularServiceA 進行賦值, 此時 circularServiceA 是在 二級緩存中。

那就比較好奇了,這時候 circularServiceC 里面的 circularServiceA 已經通過反射賦值,這個賦值給的是什么值?

查看代碼:

Spring 是如何解決循環依賴的?

 

這塊是從三級緩存(singletonFactories)中獲取的 singletonObject,然后調用

singletonObject = singletonFactory.getObject();

獲取的一個對象

Spring 是如何解決循環依賴的?

 

這里獲取到的是 circularServiceA 的引用,注意 circularServiceA 這時候還沒創建完成,只是引用。所以這里賦值的是 circularServiceA 的引用。

到這里 circularServiceC 就創建完了。

然后會將 C 添加到一級緩存和已注冊列表中,同時從二級三級緩存中刪除 C。

Spring 是如何解決循環依賴的?

 


Spring 是如何解決循環依賴的?

 

繼續執行 B 和 A 的屬性賦值以及后續的初始化流程。

至此,循環依賴解決完畢。

總結

Spring 使用三級緩存來解決循環依賴的問題,三級緩存分別是:

  • singletonObjects: 一級緩存,存儲單例對象,Bean 已經實例化,初始化完成。
  • earlySingletonObjects: 二級緩存,存儲 singletonObject,這個 Bean 實例化了,還沒有初始化。
  • singletonFactories: 三級緩存,存儲 singletonFactory。

本文也通過 Debug 來驗證了使用三級緩存解決依賴的過程。

Spring 是如何解決循環依賴的?

 

不過還有一些問題沒有說明:

  1. 循環依賴和代理之間的關系是什么?比如 @Transactional 和 @Async 注解會對循環依賴產生什么影響?
  2. 為什么要用三級緩存?二級緩存不可以么?

相關推薦

  • Spring 源碼學習 16:單例 Bean 創建
  • Spring 源碼學習 15:finishBeanFactoryInitialization(重點)
  • Spring 源碼學習 14:initApplicationEventMulticaster

引用鏈接:

[1]

Spring 官方文檔:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-dependency-resolution

分享到:
標簽:Spring
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定