spring 如何检测到循环依赖/如何解决循环依赖
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 }
總結
以上是生活随笔為你收集整理的spring 如何检测到循环依赖/如何解决循环依赖的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux学习之CentOS(二十九)-
- 下一篇: 开源血缘分析工具atlas源码讲解