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     }
原文地址:https://www.cnblogs.com/isnotnull/p/14300376.html