谈一谈spring循环依赖的问题
对于什么时循环依赖这里就不做过多的介绍
可能产生循环的三种情况
- 通过构造方法注入依赖时
这种循环依赖关系是无法解决的,spring官方文档推荐使用根据构造方法注入依赖以保证注入的依赖是一个完整的对象,但这不是绝对的,随着业务复杂总会是工造方法注入总会是产生循环以来的问题,这种循环依赖问题无法自动解决 一般可以通过 @Lazy注解实现延迟加载
类A的创建:A a=new A(B),需要依赖对象B,发现构造函数的形参上有@Lazy注解,那么就不直接创建B了,而是使用动态代理创建了一个代理类B1,此时A跟B就不是相互依赖了,变成了A依赖一个代理类B1,B依赖A。但因为在注入依赖时,类A并没有完全的初始化完,实际上注入的是一个代理对象,只有当他首次被使用的时候才会被完全的初始化。
- 通过setter方法注入依赖
- 当bean是 原型类型时,每次 getBean 都会产生一个新的对象,如果依赖链过长,getBean调用频繁很有可能发生 OOM
- 当bean是单例时,在spring内部时通过三级缓存的形式解决循环依赖的问题
单例bean存储的数据结构
在spring内部是通过下面三个map来解决 当bean是单例时 产生的循环依赖问题
/** 单例对象的缓存:bean 名称到 bean 实例. */ // 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 单例工厂缓存:bean 名称到 ObjectFactory.ObjectFactory 是一个函数式接口 */ // 三级缓存 ObjectFactory 是一个函数式接口
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 早期单例对象的缓存:bean 名称到 bean 实例. */ // 二级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
- singletonObjects 存储的是完成的bean
- earlySingletonObjects 存储的是没有完全注入依赖的bean 他可能是真是的bean 或者是 bean的代理对象
- singletonFactories 他是提前暴漏正在创建中的bean的工厂,也是通过函数式接口这种懒方式巧妙的解决循环依赖
通过三级缓存的方式解决循环依赖的主要思想就是实例化和依赖注入相分离,允许bean以半成品的形式存在。
为什么要使用三级缓存来实现呢
当然二级缓存也可以解决循环依赖,但是当bean存在代理对象时,如果只使用一级缓存、二级缓存,那么就要保证我存到二级缓存中的半成品bean是一个代理对象,那么就需要把生成代理对象的处理提前到实例化之后初始化之前。如果使用一级缓存、三级缓存就会存在创建多个代理对象的问题,不符合单例的原则
比如 A 依赖 B
B 依赖 C
B依赖 A // 产生第一个代理对象
C 依赖 A // 在这一步就会产生第二个代理对象
关于 @Async 引发的 循环依赖
spring 通过三级缓存解决循环依赖 在 singletonFactories 种创建对象时会生成代理对象,这里生成代理对象是通过AbstractAutoProxyCreator来生成的代理对象,
@EnableAsync注解之后代表可以向Spring容器中注入AsyncAnnotationBeanPostProcessor 他的 父类AbstractAdvisingBeanPostProcessor在 postProcessAfterInitialization 处理时也产生了代理对象
AsyncAnnotationBeanPostProcessor根本就不是一个SmartInstantiationAwareBeanPostProcessor 所以在 getEarlyBeanReference阶段获取对象与 AsyncAnnotationBeanPostProcessor生成的对象不是一个对象
所以在 doCreateBean 的 注册销毁方法之间检测了对象依赖