前言:大家在学习Springboot的时是否会遇到这种问题呢?
Thedependenciesof some of the beans in the application context form a****cycle
bash
//详细报错信息
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| aiCodeGeneratorServiceFactory
↑ ↓
| chatHistoryServiceImpl
↑ ↓
| appServiceImpl
↑ ↓
| aiCodeGeneratorFacade
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default.
Update your application to remove the dependency cycle between beans. As a last resort,
it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
下面讲解解决循环依赖的几种常见解决方案,可以解决99%的这种问题(尤其是最后一种方案)
方案 | 推荐度 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
重构设计 | ⭐⭐⭐⭐⭐ | 从根本上解决问题,降低耦合,提高可维护性 | 重构成本高,可能引入新的复杂性 | 循环依赖逻辑可以被抽象和分离的场景 |
@Lazy 注解 |
⭐⭐⭐⭐ | 简单、快速、侵入性低 | 治标不治本,可能隐藏初始化错误 | 快速修复,循环依赖的Bean在启动初期不被使用 |
延迟注入 | ⭐⭐⭐ | 能解决@Lazy 无法处理的复杂依赖 |
代码侵入性强,破坏依赖注入纯粹性 | 构造函数初始化复杂,无法使用@Lazy |
允许循环依赖 | ⭐⭐ | 最简单,无需修改代码 | 掩盖设计问题,未来可能失效,有潜在风险 | 仅作为临时应急方案 |
**1.**重构设计,打破循环依赖
从根本上讲,循环依赖是设计问题的症状,而非问题本身。因此,最彻底、最推荐的解决方案就是重构代码,消除这种不合理的依赖关系。这通常意味着需要对服务的职责进行更清晰的划分。
当重构代码的成本过高,推荐使用下面的解决方案
2.使用 @Lazy
注解延迟加载
将其中一个添加**@Lazy注解**,这样有一个延迟注入,从而打破了循环引用的环。
java
//字段注入
@Service
public class ServiceA {
@Autowired
@Lazy
private ServiceB serviceB;
}
//构造函数注入
@Service
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
3. 延迟注入 (@PostConstruct
)
使用 @PostConstruct
注解:
java
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
private ApplicationContext applicationContext;
@PostConstruct
public void init() {
// 在所有Bean实例化完成后,从容器中获取ServiceB
this.serviceB = applicationContext.getBean(ServiceB.class);
}
}
实现 InitializingBean
接口:
java
@Service
public class ServiceA implements InitializingBean {
private ServiceB serviceB;
@Autowired
private ApplicationContext applicationContext;
@Override
public void afterPropertiesSet() throws Exception {
// 在属性设置完成后,从容器中获取ServiceB
this.serviceB = applicationContext.getBean(ServiceB.class);
}
}
4. 配置文件允许循环依赖
application.properties:
bash
spring.main.allow-circular-references=true
application.yml:
bash
spring:
main:
allow-circular-references: true
最后的问题:大家最后了解决Bean 之间的循环依赖吗?