C#基礎知識之DI之循環依賴


注意:出現循環依賴是設計上的問題,一定要避免!

什么是DI的循環依賴

循環依賴就是循環引用,就是兩個或多個Bean之間相互引用,比如CircleA引用CircleB,CircleB引用CircleC,CircleC引用CircleA,則它們最終反映為一個環。此處不是循環調用,循環調用是方法之間的環調用。 

1、循環調用是無法解決的,除非有終結條件,否則就是死循環,最終導致內存溢出錯誤。

2、Spring容器循環依賴包括構造器循環依賴和setter循環依賴,那Spring容器如何解決循環依賴呢?首先讓我們來定義循環引用類:

 

public class CircleA {    
    private CircleB circleB; public CircleA() { } public CircleA(CircleB circleB) { this.circleB = circleB; } public void setCircleB(CircleB circleB) { this.circleB = circleB; } public void a() { circleB.b(); } } 
public class CircleB {    
    private CircleC circleC; public CircleB() { } public CircleB(CircleC circleC) { this.circleC = circleC; } public void setCircleC(CircleC circleC) { this.circleC = circleC; } public void b() { circleC.c(); } } 
public class CircleC {    
    private CircleA circleA; public CircleC() { } public CircleC(CircleA circleA) { this.circleA = circleA; } public void setCircleA(CircleA circleA) { this.circleA = circleA; } public void c() { circleA.a(); } } 

 

Spring如何解決循環依賴

1、構造器循環依賴

表示通過構造器注入構成的循環依賴,此依賴是無法解決的,只能拋出異常表示循環依賴。

如在創建CircleA類時,構造器需要CircleB類,那將去創建CircleB,在創建CircleB類時又發現需要CircleC類,則又去創建CircleC,最終在創建CircleC時發現又需要CircleA;從而形成一個環,沒辦法創建。

Spring容器將每一個正在創建的Bean 標識符放在一個“當前創建Bean池”中,Bean標識符在創建過程中將一直保持在這個池中,因此如果在創建Bean過程中發現自己已經在“當前創建Bean池”里時將拋出BeanCurrentlyInCreationException異常表示循環依賴;而對於創建完畢的Bean將從“當前創建Bean池”中清除掉。 

具體過程如下:

  • Spring容器創建“circleA” Bean,首先去“當前創建Bean池”查找是否當前Bean正在創建,如果沒發現,則繼續准備其需要的構造器參數“circleB”,並將“circleA” 標識符放到“當前創建Bean池”;
  • Spring容器創建“circleB” Bean,首先去“當前創建Bean池”查找是否當前Bean正在創建,如果沒發現,則繼續准備其需要的構造器參數“circleC”,並將“circleB” 標識符放到“當前創建Bean池”;
  • Spring容器創建“circleC” Bean,首先去“當前創建Bean池”查找是否當前Bean正在創建,如果沒發現,則繼續准備其需要的構造器參數“circleA”,並將“circleC” 標識符放到“當前創建Bean池”;
  • 到此為止Spring容器要去創建“circleA”Bean,發現該Bean 標識符在“當前創建Bean池”中,因為表示循環依賴,拋出BeanCurrentlyInCreationException。

  

2、setter循環依賴

表示通過setter注入方式構成的循環依賴。

對於setter注入造成的依賴是通過Spring容器提前暴露剛完成構造器注入但未完成其他步驟(如setter注入)的Bean來完成的,而且只能解決單例作用域的Bean循環依賴。

具體步驟如下:

  • Spring容器創建單例“circleA” Bean,首先根據無參構造器創建Bean,並暴露一個“ObjectFactory ”用於返回一個提前暴露一個創建中的Bean,並將“circleA” 標識符放到“當前創建Bean池”;然后進行setter注入“circleB”;
  • Spring容器創建單例“circleB” Bean,首先根據無參構造器創建Bean,並暴露一個“ObjectFactory”用於返回一個提前暴露一個創建中的Bean,並將“circleB” 標識符放到“當前創建Bean池”,然后進行setter注入“circleC”;
  • Spring容器創建單例“circleC” Bean,首先根據無參構造器創建Bean,並暴露一個“ObjectFactory ”用於返回一個提前暴露一個創建中的Bean,並將“circleC” 標識符放到“當前創建Bean池”,然后進行setter注入“circleA”;進行注入“circleA”時由於提前暴露了“ObjectFactory”工廠從而使用它返回提前暴露一個創建中的Bean;
  • 最后在依賴注入“circleB”和“circleA”,完成setter注入

注意:出現循環依賴是設計上的問題,一定要避免!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM