spring如何解决循环依赖的
什么是循环依赖
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。
解决循环依赖的步骤
以Bean A和Bean B相互依赖为例:
创建Bean A:
实例化A(调用构造器,此时对象尚未初始化)。
将A的工厂对象(用于生成早期引用)放入三级缓存。
开始填充A的属性,发现需要注入Bean B。
创建Bean B:
实例化B,同样将B的工厂对象放入三级缓存。
填充B的属性时,发现需要注入Bean A。
解决A的早期引用:
从三级缓存中找到A的工厂对象,调用getObject()生成A的早期引用(可能是原始对象或代理对象)。
将A的早期引用放入二级缓存,并从三级缓存移除其工厂对象。
此时Bean B完成属性注入,初始化后放入一级缓存。
完成Bean A的初始化:
Bean A获取到已初始化的Bean B,继续完成属性注入和初始化。
将A从二级缓存移到一级缓存,供后续使用。
三级缓存
就是三个Map
一级缓存:放的是最终创建完成的Bean
二级缓存:避免多重循环依赖的情况下,重复创建动态代理;
三级缓存:
函数接口:通过lambda把方法传进去(把Bean实例+名字传递进去;AOP创建)
不会立即调用
在ABA第二次调用getBean(A)才会调用三级缓存(实现AOP就会返回代理对象;否则就会返回Bean实例)
放入二级缓存避免重复创建
Spring通过三个缓存存储不同阶段的Bean对象:
一级缓存(Singleton Objects):存放完全初始化好的Bean,可直接使用。
二级缓存(Early Singleton Objects):存放早期暴露的Bean(已实例化但未完成属性注入和初始化),用于解决循环依赖。
三级缓存(Singleton Factories):存放Bean工厂对象(ObjectFactory),用于生成早期Bean的引用(可能包含代理对象)。
无法被解决场景
构造注入
ABA中,A创建的临时对象的构造器中有别的依赖,因此无法完成创建临时对象,所以无法走出循环依赖
【并不是必须要AB均为构造注入才可以】
@Component
public class BeanA {
private final BeanB beanB;
@Autowired
public BeanA(BeanB beanB) { // 构造器注入BeanB
this.beanB = beanB;
}
}
@Component
public class BeanB {
private final BeanA beanA;
@Autowired
public BeanB(BeanA beanA) { // 构造器注入BeanA
this.beanA = beanA;
}
}
异常信息
BeanCurrentlyInCreationException:
Error creating bean with name 'beanA':
Requested bean is currently in creation: Is there an unresolvable circular reference?
| 依赖情况 | 依赖注入方式 | 循环依赖是否被解决 |
|---|---|---|
| AB相互依赖(循环依赖) | 均采用setter方法注入 | 是 |
| AB相互依赖(循环依赖) | 均采用构造器注入 | 否 |
| AB相互依赖(循环依赖) | A中注入B的方式为setter方法,B中注入A的方式为构造器 | 是 |
| AB相互依赖(循环依赖) | A中注入B的方式为构造器,B中注入A的方式为setter方法 | 否 |
参考: https://www.cnblogs.com/daimzh/p/13256413.html
https://blog.csdn.net/wangxuelei036/article/details/104960558
原型作用域(Prototype Scope)Bean的循环依赖
- 原型Bean(@Scope("prototype"))每次请求都会生成新实例,Spring不会缓存这些Bean的早期引用。
- 三级缓存机制仅针对单例Bean,原型Bean无法通过缓存提前暴露引用,导致循环依赖无法解决
混合作用域的循环依赖
单例Bean依赖原型Bean,而原型Bean又反向依赖单例Bean
复杂代理逻辑,@Async或AOP代理的特殊情况
如果循环依赖的Bean中某个Bean被AOP代理(如@Async、@Transactional),且代理生成逻辑复杂,可能导致早期引用与实际对象不一致
循环依赖始终是代码写的烂
SpringBoot2.6以后,默认禁止了循环依赖
几种强制解决的办法
本质都是 延迟注入
- 避免使用构造器注入的循环依赖。 改用Setter方法注入或字段注入(@Autowired);重新设计代码结构,解耦相互依赖的Bean。
- @Lazy
TODO结合代码看具体的过程
https://blog.csdn.net/weixin_48306950/article/details/121639656

浙公网安备 33010602011771号