循环依赖的定义
循环依赖指两个或多个Bean相互依赖,形成闭环。例如Bean A依赖Bean B,而Bean B又依赖Bean A,导致Spring容器无法正常初始化。
使用构造函数注入避免循环依赖
Spring官方推荐优先使用构造函数注入而非字段注入。构造函数注入在启动时就能检测到循环依赖并抛出异常,避免运行时问题。
java
@Service
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private final ServiceA serviceA;
@Autowired
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
此时启动会直接报错:Requested bean is currently in creation。
使用@Lazy延迟加载
在其中一个依赖上添加@Lazy注解,延迟依赖的初始化,打破循环链。
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;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
重新设计代码结构
最佳实践是通过中间层或接口解耦:
- 提取公共逻辑到第三方Bean
- 使用事件驱动(
ApplicationEventPublisher) - 采用面向接口编程
调整Bean加载顺序
通过@DependsOn显式指定加载顺序:
java
@Service
@DependsOn("serviceB")
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
注意事项
- Spring默认允许单例Bean的循环依赖,但原型(Prototype)作用域的Bean会直接报错
- 循环依赖可能暴露未完全初始化的Bean,导致NPE风险
- 建议通过Sonar等工具检测循环依赖代码坏味道