Spring循环依赖相关
循环依赖的概念
循环依赖简单的来说就是多个依赖相互引用,造成bean在相互诸如的时候出现死循环,为了解决这个问题,Spring提供了循环依赖的解决办法:利用三级缓存来解决
三级缓存适用前提
三级缓存也不是能够解决所有情况下的循环依赖,以下这些问题无法被循环依赖解决
- 在多实例化的情况下的bean,并且bean是通过setter方法注入的话,出现了循环依赖问题是无法解决的。
- 构造器注入的情况下也不能解决循环依赖问题.
- 单例的单例bean通过setter注入bean不能解决循环依赖问题
- 设置了@dependeceon的注解是不能解决循环依赖问题
三级缓存原理
三级缓存实际上是三个map,分别为SigletonObject、earlySingletonObject、SingletonFactories,他们的作用如下:
名称 | 描述 |
---|---|
singletonObjects | 一级缓存,存放完整的 Bean。 |
earlySingletonObjects | 二级缓存,存放提前暴露的Bean,Bean 是不完整的,未完成属性注入和执行 init 方法。 |
singletonFactories | 三级缓存,存放的是 Bean 工厂(代理bean),主要是生产 Bean,存放到二级缓存中。 |
三级缓存解决循环依赖的整个流程解释:
spring中循环依赖的场景比较多,比如bean的生命周期中的给实例化的对象中的属性赋值就是一个循环依赖的场景。我们知道一个bean的生命周期包括实例化bean、给bean中的属性赋值、赋完值再进行后续操作、之后再加入单例池、再使用、再销毁这些步骤。
假如对象为A的bean依赖于对象为B的bean,而对象为B的bean也依赖于对象为A的bean,那么假设A实例化后,它要对属性赋值,此时有些属性依赖于B,那么它又要实例化B,之后给B的属性赋值,此时B依赖于A,如果没有循环依赖的话这里就会循环的实例化下去,这样肯定是不行的,因此有循环依赖的话就会先去找到之前的A仅仅进行了实例化的bean,这个bean传入在第三级缓存中,这时候B在一级缓存SingletonObject里面没有找到A,那它就会去二级缓存里面找,二级缓存也没有,那么就从三级缓存利用bean工厂生成bean代理对象存放到二级缓存中,之后从二级缓存拿到A的代理对象后B就会继续进行后面的操作得到一个完整的bean,并把完整的对象为B的bean存入一级缓存,此时A就会从一级缓存中拿到对象为B的bean,也能进行后续操作并且从二级缓存放到一级缓存。
为什么需要三级缓存
如果 Spring 选择二级缓存来解决循环依赖的话,那么就意味着所有 Bean 都需要在实例化完成之后就立马为其创建代理,而 Spring 的设计原则是在 Bean 初始化完成之后才为其创建代理。所以,Spring 选择了三级缓存。但是因为循环依赖的出现,导致了 Spring 不得不提前去创建代理,因为如果不提前创建代理对象,那么注入的就是原始对象,这样就会产生错误。
其他解决循环依赖的方法
@Lazy 注解也能解决循环依赖。
Spring 能解决构造函数循环依赖吗
答案是不行的,对于使用构造函数注入产生的循环依赖,Spring 会直接抛异常。为什么无法解决构造函数循环依赖?
上面解决逻辑的第一句话:“首先使用构造函数创建一个 “不完整” 的 bean 实例”,从这句话可以看出,构造函数循环依赖是无法解决的,因为当构造函数出现循环依赖,我们连 “不完整” 的 bean 实例都构建不出来。