Spring无法解决循环依赖的五种场景分析
目录
- 一、构造器注入引发的循环依赖
- 1. 问题复现
- 2. 原理分析
- 3. 解决方案
- 二、原型(Prototype)作用域的循环依赖
- 1. 问题复现
- 2. 原理分析
- 3. 解决方案
- 三、@Async 注解导致的代理冲突
- 1. 问题复现
- 2. 原理分析
- 3. 解决方案
- 四、Configuration 类之间的循环依赖
- 1. 问题复现
- 2. 原理分析
- 3. 解决方案
- 五、自定义 BeanPostProcessor 引发的冲突
- 1. 问题复现
- 2. 原理分析
- 3. 解决方案
- 六、终极解决方案工具箱
- 结语:跳出循环依赖的思维陷阱
一、构造器注入引发的循环依赖
1. 问题复现
@Component
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) { // 构造器注入
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
private final ServiceA serviceA;
@Autowired
public ServiceB(ServiceA serviceA) { // 构造器注入
this.serviceA = serviceA;
}
}
报错信息:Requested bean is currently in creation: Is there an unresolvable circular reference?
2. 原理分析
- 三级缓存失效:构造器注入要求在实例化阶段完成依赖注入,而此时 Bean 尚未放入三级缓存。
- 生命周期冲突:

3. 解决方案
- 方案 1:将其中一个 Bean 改为 Setter / 字段注入
- 方案 2:使用 `@Lazy` 延迟加载
@Autowired
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
二、原型(Prototype)作用域的循环依赖
1. 问题复现
@Scope("prototype")
@Component
public class PrototypeA {
@Autowired pandroidrivate PrototypeB b;
}
@Scope("prototype")
@Component
public class PrototypeB {
@Autowired private PrototypeA a;
}
2. 原理分析
缓存机制不生效:原型 Bean 不会存入三级缓存,每次请求都创建新实例。
Spring 官方限制:明确说明js不处理原型 Bean 的循环依赖。
3. 解决方案
重构设计:避免原型 Bean 之间的循环依赖
改用单例:评估是否真的需要原型作用域
三、@Async 注解导致的代理冲突
1. 问题复现
@Service
public class AsyncServiceA {
@Autowired private AsyncServiceB serviceB;
@Async
public void asyncMethod() { /* ... */ }
}
@Service
public class AsyncServiceB {
@Autowired private AsyncServiceA serviceA;
}
2. 原理分析
代理时序问题:
@Async通过后置处理器生成代理,可能破坏三级缓存机制。典型错误栈:
BeanCreationException: Error creating bean with name 'asyncServiceA': Bean with name 'asyncServiceA' has been injected into other beans [...] in their raw version as part of a circular reference.
3. 解决方案
- 方案 1:对异步方法所在类使用接口代理
@Async
public interface AsyncService {
void asyncMethod();
}
@Service
public class AsyncServiceImpl implements Asy编程客栈ncService { /* ... */ }
- 方案 2:在注入点添加
@Lazy
@Autowired @Lazy private AsyncServiceA serviceA;
四、Configuration 类之间的循环依赖
1. 问题复现
@Configuration
public class ConfigA {
@Autowired private ConfigB configB;
}
@Configuration
public class ConfigB {
@Autowired private ConfigA configA;
}
2. 原理分析
配置类加载顺序:配置类需要优先初始化,无法通过常规循环依赖解决。
Spring 限制:
@Configuration类被视为特殊 Bean,其代理机制与普通 Bean 不同。
3. 解决方案
重构配置类:合并相关配置
使用
@DependsOn:明确指定加载顺序
@Configuration
@DependsOn("configB")
public class ConfigA { /* ... */ }
五、自定义 BeanPostProcessor 引发的冲突
1. 问题复现
@Component
public class CustomProcessor implements BeanPostProcessor {
@Autowired private ServiceX x; // 依赖其他Bean
}
2. 原理分析
处理www.devze.com器加载时序:
BeanPostProcessor需要优先初始化,此时普通 Bean 尚未创建。Spring 启动流程:

3. 解决方案
避免在 BeanPostProcessor 中注入其他 Bean
使用延迟注入
private ObjectProvider<ServiceX> xProvider;
public Object postProcessBeforeInitialization(Object bean, String beanName) {
ServiceX x = xProvider.getIfAvailable();
// ...
}
六、终极解决方案工具箱
| 问题类型 | 应急方案 | 根治方案 |
|---|---|---|
| 构造器循环依赖 | @Lazy 注解 | 改为 Setter 注入 |
| 原型Bean循环依赖 | 重构作用域 | 引入中间类抽象依赖 |
| AOP代理冲突 | 接口代理模式 | 调整切面作用顺序 |
| 配置类循环依赖 | @DependsOn 指定顺序 | 合并配置类 |
| BeanPostProcessor依赖 | ObjectProvider 延迟获取 | 分离处理器与业务逻辑 |
结语:跳出循环依赖的思维陷阱
Spring 的循环依赖处理机制体现了框架设计的高度智慧,但作为开发者,最优雅的解决方案往往不是技术手段,而是架构设计。通过以下原则可从根本上避免循环依赖:
单一职责原则:拆分臃肿的 Bean
依赖倒置原则:面向接口编程
层次化设计:Controller -> Service -> Repository 的严格分层
以上就是Spring无法解决循环依赖的五种场景分析的详细内容,更多关于Spring无法解决循环依赖的资料请关注编程客栈(www.devze.com)编程客栈其它相关文章!
加载中,请稍侯......
精彩评论