spring 如何解决循环依赖

什么是循环依赖问题?
  • A 依赖 B ,B 依赖 A
spring 解决循环依赖的思路
  • 假设对象A中有属性是对象B,对象B中也有属性是对象A,A和B循环依赖
    • 创建对象A,调用A的构造,并把A保存下来
    • 然后准备注入对象A中的依赖,发现对象A依赖对象B,那么开始创建对象B,调用B的构造,并把B保存下来
    • 然后准备注入B的构造,发现B依赖对象A,对象A之前已经创建了,直接获取A并把A注入B
    • 注意此时的对象A还没有完全注入成功,对象A中的对象B还没有注入,B创建成功,创建成功的B注入A,A也创建成功
    • 于是循环依赖就被解决
spring 的源码实现
  • getSingleton() 方法处理循环依赖问题请添加图片描述
  • 分析 getSingleton() 方法
    • Object singletonObject = this.singletonObjects.get(beanName)

      • 方法首先从singletonObjects中获取对象,当Spring准备新建一个对象时,singletonObjects列表中是没有这个对象的,然后进入下一步
    • if (singletonObject == null && isSingletonCurrentlyInCreation(beanName))

      • 除了判断 null 之外,有一个 isSingletonCurrentlyInCreation 的判断,实际上当 spring 初始化一个依赖注入的对象,但还没注入对象属性的时候,spring 会把这个 bean 加入singletonsCurrentlyInCreation 这个 set 中,也就是把这个对象标记为正在创建的状态,这样,如果 spring 发现要创建的 bean 在 singletonObjects 中没有,但在singletonsCurrentlyInCreation 中有,基本上就可以认定为循环依赖(在创建 bean 的过程中发现又要创建这个 bean,说明 bean 的某个依赖又依赖这个 bean,即循环依赖)
    • singletonObject = this.earlySingletonObjects.get(beanName)

      • 这里引入 earlySingletonObjects 列表,这是个为了循环依赖而存在的列表,从名字就可以看到,是个预创建的对象列表,刚刚创建的对象在这个列表里一般也没有
    • if (singletonFactory != null) {
      singletonObject = singletonFactory.getObject();
      this.earlySingletonObjects.put(beanName, singletonObject);
      this.singletonFactories.remove(beanName);
      }

      • 代码到这里基本已经确定我们要创建的这个对象已经发生循环依赖了,然后 spring 进行这样的操作,把这个对象加入到 earlySingletonObjects 中,然后把该对象从 singletonFactories 中删掉
    • 前面步骤已经执行完该方法的代码,这里加的步骤是为了解释循环依赖的结果;在这个方法的代码之后,会把 bean 完整的进行初始化和依赖的注入,在完成 bean 的初始化后,后面代码逻辑中会调用一个这样的方法 getSingleton(String beanName, ObjectFactory<?> singletonFactory),该方法中有一个子方法

      • protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
        }
        }
        这个方法处理的是已经注入完依赖的 bean,把 bean 放入 singletonObjects 中,并把 bean 从earlySingletonObjects 和 singletonFactories 中删除,这个方法和上面分析的方法组成了 spring处理循环依赖的逻辑
总结
  • 缓存级别
    • singletonObjects 一级缓存:存储完整的 bean 对象
    • earlySingletonObjects 二级缓存:存储半成品的 bean 对象
    • singletonFactories 三级缓存:存储实例化的 bean 对象,但是还没初始化
  • 解决步骤
    • 开始初始化对象A
    • 调用A的构造,把A放入 singletonFactories
    • 开始注入A的依赖,发现A依赖对象B
    • 开始初始化对象B
    • 调用B的构造,把B放入 singletonFactories
    • 开始注入B的依赖,发现B依赖对象A
    • 开始初始化对象A,发现A在 singletonFactories 里有,则直接获取A
    • 把A放入 earlySingletonObjects,把A从 singletonFactories 删除
    • 对象B的依赖注入完成
    • 对象B创建完成,把B放入 singletonObjects
    • 把B从 earlySingletonObjects 和 singletonFactories 中删除
    • 对象B注入给A,继续注入A的其他依赖,直到A注入完成
    • 对象A创建完成,把A放入 singletonObjects,
    • 把A从 earlySingletonObjects 和 singletonFactories 中删除
    • 循环依赖处理结束,A和B都初始化和注入完成
延伸问题
  • 如果要使用二级缓存解决循环依赖,意味着所有 bean 在实例化后就要完成 aop 代理,这样违背 spring 设计的原则,spring 在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator 这个后置处理器来在 bean 生命周期的最后一步来完成 aop 代理,而不是在实例化后就立马进行 aop 代理
参考博客

版权声明:本文为qq_41956014原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。