循环依赖问题分析与解决

Catalogue
  1. 1. 循环依赖分类
  2. 2. 三级缓存
  3. 3. 源码分析
  4. 4. 自动解决循环依赖
  5. 5. @Lazy注解原理
  6. 6. 参考资料

循环依赖就是在多个bean中,相互持有对方,导致在创建的时候无法加载。如:beanA引用了beanB,beanB又引用了beanC,beanC最后又引用回了beanA,成了一个无限的循环。循环依赖是对象与对象之间才会发生的,而方法之间的相互调用的情况,叫做循环调用,此招无解最终会因为方法之间调用过多导致内存溢出。

循环依赖分类

实际spring将循环依赖细分为了三种,并且spring只能解决在setter下的循环依赖,而无法解决其他两种情况。

  • 构造器循环依赖: 通过构造方法注入bean时发生的
  • setter循环依赖:通过setter方法
  • prototype作用域循环依赖

三级缓存

  • singletonObjects:完整的单例对象的缓存,即bean依赖也注入过的
  • singletonFactories : 仅实例化完成后的,返回的是单例对象工厂对象缓存,并未组装依赖关系
  • earlySingletonObjects :提前暴光的单例对象的缓存,注入过程中的对象

源码分析

  1. 通过配置文件或@Bean,配合ComponentScan生成BeanDefinition
  2. 通过反射实例化bean
  3. 组装依赖关系,一个关键方法pupulateBean,会从BeanFactory中查找要依赖的bean,如果不存在,就会先去实例化,体现了懒加载的思想

自动解决循环依赖

  1. A的创建: A a = new A();
  2. 属性注入: 发现需要B
  3. 创建B b=new B();
  4. 属性注入: 发现需要A,此时A已经创建了.只是还没经过各种后置处理器处理,所以B是可以完成属性注入的,只是一个半成品
  5. 之后回到第2步,就可以给A赋值了,循环依赖到此解决

@Lazy注解原理

  1. A的创建: A a=new A();
  2. 属性注入: 发现需要B,查询字段b的所有注解,发现有@lazy注解,那么就不直接创建B了,而是使用动态代理创建一个代理类B
  3. 此时A跟B就不是相互依赖了,变成了A依赖一个代理类B1,B依赖A

参考资料