spring源碼解析 - spring緩存和循環依賴問題

前言

由上篇的分析可知,spring建立和實例化bean的過程的環節是比較多而且包裝比較深的,那麼若是每次getBean時都須要走這麼多環節的話,那麼不但會產生不少內存對象和計算邏輯,並且更重要的是沒法解決對象在一些場景中的依賴問題,尤爲是循環依賴的問題。所以spring自己也考慮到了這個問題,在建立bean的過程當中會有一些相關的緩存設計。今天咱們就一塊兒來看一下它是如何用緩存來解決的。spring

緩存場景

場景一、首次getBean()時
spring源碼解析 - spring緩存和循環依賴問題
首次獲取bean時,根據上圖的流程當從一級或二級緩存中獲取,若拿到則直接返回。若沒有則直接從三級緩存中拿,當三級緩存中有緩存對象時,則經過緩存的beanFactory .getObject()直接拿到bean, 同時移除三級緩存將拿到的bean寫入二級緩存中,而後返回對象。
spring源碼解析 - spring緩存和循環依賴問題
spring源碼解析 - spring緩存和循環依賴問題
場景二、首次createBean()時
spring源碼解析 - spring緩存和循環依賴問題
spring源碼解析 - spring緩存和循環依賴問題
在doCreateBean()過程當中,若bean設置可提早暴露(默認開啓),則會建立三級緩存(beanFactory對象)
spring源碼解析 - spring緩存和循環依賴問題
spring源碼解析 - spring緩存和循環依賴問題
爲何會在建立三級緩存時,同時移除二級緩存?由於二級緩存中的對象是從三級緩存中獲取的,因此當三級緩存更新時會同時移除老舊的二級緩存數據,避免產生緩存數據不一致問題。緩存

循環依賴

在A對象中,持有對B對象的引用;同理在B對象中,也支持對A對象的引用。這種循環依賴分兩種狀況:1.在A對象中,B對象做爲A對象的成員變量;2.在A對象中,B對象做爲A對象的構造參數依賴, 這兩種方式一旦有相互成環的引用場景,就須要引發重視了。
spring源碼解析 - spring緩存和循環依賴問題
以上截圖屬第一種狀況,spring也只有對這種狀況有考慮。這種狀況spring視爲正常引用,其它狀況spring不支持且有此狀況會直接拋出異常。
spring源碼解析 - spring緩存和循環依賴問題
spring源碼解析 - spring緩存和循環依賴問題ide

解決方案

那麼對於以上的第一種循環依賴狀況,spring是如何解決的呢?經過構建三級緩存機制完美解決這個問題。具體的解決過程請看下面的流程圖
spring源碼解析 - spring緩存和循環依賴問題
根據上面的解決流程圖和貼出的緩存源碼, 詳細的解釋以下:函數

  1. 當CircularRefA實例化時,先從緩存種拿實例bean;
  2. 當三個級別的緩存中都不存在對象時調用getSingleton()建立實例;
  3. 調用匿名類建立實例createBean();
  4. 經過構造函數實例化CircularRefA對象(對象中的依賴屬性和成員沒有被實例化,也就是CircularRefB對象這時是null);
  5. 建立第三級緩存,緩存CircularRefA對應的objectFactory對象;
  6. 對CircularRefA進行依賴注入,這時觸發CircularRefB對象getBean()操做;
  7. 程序遞歸進入步驟1....步驟6;
  8. 當同理到步驟6時,在CircularRefB中對CircularRefA進行依賴注入, 觸發getBean()操做;
  9. 這時從第三級緩存中能夠拿到CircularRefA的引用(雖然只是個骨架),因而CircularRefA被注入;
  10. 緊接着CircularRefB實例化完成,而後經過依賴注入賦值給CircularRefA中的CircularRefB成員變量;
  11. 最終CircularRefA和CircularRefB兩個對象都實例化完成(他們彼此持有對方的引用,這時也會被賦值上);

OK, 看到這裏 你們是否是明白了?spring的三個等級的緩存機制設計是很值得玩味的。感興趣的能夠深刻去看下源碼。
最後要明確一個結論:只有beandefintion的Scope=Singleton類型才能進行緩存和支持以上的第一種狀況的循環依賴,其它類型的只要出現循環依賴,spring直接拋出異常。至於其它狀況爲何spring不支持你們看過源碼,應該或多或少能想獲得。設計

結束語

關於spring緩存和循環依賴的問題就先分享到這裏。有什麼關於這塊的問題能夠直接在評論區留言,更多spring源碼的乾貨請繼續關注!3d

相關文章
相關標籤/搜索