spring針對循環依賴問題 不能完全解決 對於不能解決的只能檢測到並拋出異常
1. spring針對構造器方法的 單實例對象和原型對象是無法解決循環依賴問題的
先說結論,
針對單例對象 getSingleton方法中 有個beforeSingletonCreation 方法 這個方法是用來檢測循環依賴的
原型對象 isPrototypeCurrentlyInCreation方法beforePrototypeCreation方法配合檢測循環依賴
注: inCreationCheckExclusions和singletonsCurrentlyInCreation 是兩個set
1 protected void beforeSingletonCreation(String beanName) { 2 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { 3 throw new BeanCurrentlyInCreationException(beanName); 4 } 5 }
以AB兩個單例對象舉例,
1 Class A(){ 2 A(B b){ 3 } 4 } 5 6 Class B(){ 7 B(A a){ 8 } 9 }
對象A在實例化的時候(getBean方法),會先執行beforeSingletonCreation方法 吧自己的beanName放入set中,然后去執行實例化 解析構造器方法的時候發現 需要用到對象B ,所以去getBean(B)
B對象在一級緩存是沒有的(因為是還未實例化),所以去創建單例B,會執行和A一樣的操作,把自己的beanName放入set中,然后解析構造器的時候發現依賴A對象,去一級緩存獲取是沒有的(因為A對象實例化還未完成 未放入到一級緩存中)
所以去實例化A 放入set的時候 發現已經存在及會拋出異常
1 protected boolean isPrototypeCurrentlyInCreation(String beanName) { 2 Object curVal = this.prototypesCurrentlyInCreation.get(); 3 return (curVal != null && 4 (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); 5 }
1 protected void beforePrototypeCreation(String beanName) { 2 Object curVal = this.prototypesCurrentlyInCreation.get(); 3 if (curVal == null) { 4 this.prototypesCurrentlyInCreation.set(beanName); 5 } 6 else if (curVal instanceof String) { 7 Set<String> beanNameSet = new HashSet<>(2); 8 beanNameSet.add((String) curVal); 9 beanNameSet.add(beanName); 10 this.prototypesCurrentlyInCreation.set(beanNameSet); 11 } 12 else { 13 Set<String> beanNameSet = (Set<String>) curVal; 14 beanNameSet.add(beanName); 15 } 16 }
對象在實例化之前會先調用isPrototypeCurrentlyInCreation方法,如果set中沒有繼續執行,實例化的時候調用beforePrototypeCreation方法 放入set中,在之后的循環依賴中 查詢到set中有自己的beanName 則拋出異常
那么spring對set方法注入 是如何解決循環依賴問題的呢,一樣先說結論 是使用第三級緩存來解決的
1. 在實例化bean A的時候 先去查看一級緩存中是否有(執行的方法getSingleton)
並且會查詢currentlyCreationSet中有沒有
2. 處理一下depend-on依賴 或者檢查一下是否是抽象等等安全性校驗
3.把單實例的A 的beanName放入currentlyCreationSet中
4. 實例化A對象因為是set注入 所以這時使用的空參構造 去反射出實例對象 得到一個早期對象(未進行屬性注入的對象,未執行init方法調用,未進行后處理器處理,早期對象和處理后的對象的內存地址是一樣的aop對象除外)
5. 早期實例A封裝到objectFactory對象中,放入三級緩存
6. 進行依賴注入 依賴注入的時候發現依賴Bean B 再去執行getBean方法 重復執行1-6步 拿到一個B的早期對象objectFactory 放入到三級緩存
7. 處理B的依賴注入 發現B依賴了A 執行getbean方法 從1開始執行 去一級緩存查詢沒有 去currentlyCreationSet查詢有A 去三級緩存中拿到A的早期實例 放入二級緩存清除三級緩存中的A早期實例並返回這個早期實例 B繼續執行完成了B對象的實例 放入一級緩存中 清除掉二三級緩存相關數據
8. 繼續執行A的實例化步驟 A完成實例化 存入一級緩存 清除二三級緩存相關數據
1 protected Object getSingleton(String beanName, boolean allowEarlyReference) { 2 Object singletonObject = this.singletonObjects.get(beanName); 3 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { 4 synchronized (this.singletonObjects) { 5 singletonObject = this.earlySingletonObjects.get(beanName); 6 if (singletonObject == null && allowEarlyReference) { 7 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); 8 if (singletonFactory != null) { 9 singletonObject = singletonFactory.getObject(); 10 this.earlySingletonObjects.put(beanName, singletonObject); 11 this.singletonFactories.remove(beanName); 12 } 13 } 14 } 15 } 16 return singletonObject; 17 }
