循环依赖的概念
循环依赖是指两个或多个Bean相互依赖,形成闭环。例如Bean A依赖Bean B,Bean B又依赖Bean A。Spring默认情况下会抛出BeanCurrentlyInCreationException异常。
解决方法
使用构造器注入 + @Lazy注解
在构造器注入的场景下,可以通过@Lazy延迟加载其中一个Bean来打破循环:
java
@Service
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
改用Setter/Field注入
Spring官方推荐使用构造器注入,但Setter/Field注入能自动处理循环依赖:
java
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
调整代码结构
最彻底的解决方案是重构代码,提取公共逻辑到第三个Bean中,或使用接口分离依赖关系。
Spring处理循环依赖的机制
Spring通过三级缓存解决Setter/Field注入的循环依赖:
- 一级缓存:存放完整初始化的Bean
- 二级缓存:存放早期暴露的原始Bean
- 三级缓存:存放Bean工厂,用于生成原始Bean
注意事项
- 构造器注入的循环依赖必须显式解决
- 循环依赖可能掩盖设计问题,应优先考虑重构
- 高版本Spring Boot默认禁止循环依赖,可通过配置允许:
properties
spring.main.allow-circular-references=true
推荐做法
对于必须存在的循环依赖,建议:
- 使用
@Lazy注解 - 保持最少量的循环关系
- 添加文档说明循环的必要性
- 监控循环依赖Bean的性能影响