spring不支持构造器注入的循环依赖,因其要求实例化时依赖必须就绪,导致a依赖b、b依赖a时陷入死锁;而字段或setter注入可通过三级缓存暴露半成品对象来解耦。

Spring 默认不支持构造器注入的循环依赖,根本原因在于对象创建逻辑无法拆解:构造器注入要求在实例化 Bean 的瞬间,所有依赖必须已就绪;而两个互相依赖的 Bean 都卡在“等对方先造出来”的状态,形成死锁。
这和字段或 setter 注入完全不同——后者允许 Spring 先 new 出原始对象(实例化),再逐步填充属性(依赖注入),中间靠三级缓存提前暴露“半成品”来打破闭环。
构造器循环依赖为什么必报错?
- Spring 在创建 Bean A 时,发现其构造器需要 B;
- 转头去创建 B,又发现 B 的构造器需要 A;
- 此时 A 还没完成初始化,甚至还没填完属性,更不可能作为完整依赖传给 B;
- Spring 检测到 A 已在“正在创建中”集合里(
singletonsCurrentlyInCreation),直接抛出BeanCurrentlyInCreationException。
简单说:构造器注入 = 创建即完成依赖绑定,没有“中间态”可利用。
三种实用处理方式
改用字段或 setter 注入
把private final XxxService service;+ 构造器参数,换成@Autowired private XxxService service;或带@Autowired的 setter 方法。Spring 天然支持,无需额外配置。加 @Lazy 到构造器参数上
例如:public OrderService(@Lazy PayService payService)。Spring 会注入一个临时代理对象,首次调用时才真正初始化 PayService,绕过创建时的依赖检查。手动通过 ApplicationContext 获取依赖
让类实现ApplicationContextAware,在setApplicationContext()中调用ctx.getBean(Xxx.class)。适合少量、特殊场景,但破坏了依赖注入的清晰性,不推荐常规使用。
补充提醒
- Spring Boot 2.6+ 默认禁止循环依赖(
spring.main.allow-circular-references=false),若坚持用,需显式设为true,但这只是掩盖问题,不是解决设计缺陷; - 真正治本的方法是重构:比如提取公共逻辑到第三个服务、用事件机制替代直接调用、或引入回调/策略接口解耦。
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











