Spring循环依赖问题

Spring 产生循环依赖问题的前提条件

  1. Spring管理的Bean默认都是单例的,这些对象在Spring容器里面只有唯一一份,所以Spring创建bean的时候就必须要考虑到不能重复创建对象,否则也就违背了单例的原则,所以这个时候就需要考虑到循环依赖的情况

Spring解决循环依赖的思路

  1. Spring 是通过三级缓存Map,和一个registeredSingletons(标记缓存: 保存着正在创建中的对象的名称)来解决循环依赖的

Spring创建对象的两个阶段

  1. 对象的实例化
  2. 对象属性的注入(注入阶段会引起循环依赖)

Spring的三级缓存

  1. 一级缓存:保存所有已经创建完成的bean
    • private final Map singletonObjects = new ConcurrentHashMap<>(256)
  2. 二级缓存:保存正常创建中的bean(完成了实例化,但是还未完成属性注入)
    • private final Map earlySingletonObjects = new HashMap<>(16)
  3. 三级缓存:保存的是ObjectFactory类型的对象的工厂,通过工厂的方法可以获取到目标对象
    • private final Map> singletonFactoies = new HashMap<>(16)
  4. 标记缓存:保存着正在创建中的对象名称

为什么需要三个缓存而不是两个缓存

  1. 可以通过第三级Map的value属性作为参考,存放的是ObjectFactory对象
  2. ObjectFactory对象在调用getObject的时候,会(需要)处理代理类的包装和替换
  3. 即第三级缓存中还存在不确定性,所以需要三级

Spring无法解决循环依赖的场景

  1. 因为二级缓存中存放的是完成了实例化的对象,完成了实例化的对象放入二级缓存是解决循环依赖不可缺少的一环,但是对象的实例化是通过调用构造函数实例化的(即构造和注入放到一起的时候),所以如果是通过构造函数进行DI且有循环依赖的情况的话,Spring是无法解决的
  2. 类中有@Async注释的方法的话,Spring也无法解决循环依赖问题,原因是对于定义了Async方法的场景,Spring会新增一个后置处理器AsyncAnnotationBeanPostProcessor,其生成的代理对象和前面的对象不同,并且该后置处理器的顺序偏后
  3. @Lazy注解可以解决上述的两种循环依赖问题,其原理是Lazy修饰的属性,Spring会先为其创建一个代理对象而不是一个原始对象
    1. https://blog.csdn.net/WX10301075WX/article/details/123904543
    2. https://blog.csdn.net/yxh13521338301/article/details/117450763

无法解决循环问题场景的总结

  1. 无法进行实例化(如构造方法DI场景)
  2. 由于代理类与加载滞后的问题所引发的,拿不到同一个对象而照成的情况

最终总结

从SpringBoot 2.6.0 之后Spring官方已经不建议循环依赖了,从业务或者说包、Service类划分上去解决循环依赖才是一个相对正规的路径

你可能感兴趣的:(Java,Spring,spring,java,后端,spring,boot,循环依赖)