一、构造方法注入的循环依赖:❌ 完全不支持
问题本质
- 死锁场景:对象未实例化前就必须完成构造器调用,而构造参数依赖的对象同样未被创建
- Spring 的设计约束:必须在对象实例化(调用构造器)后才能暴露早期引用(三级缓存机制的前提)
源码验证(AbstractAutowireCapableBeanFactory
)
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// 构造器注入会直接尝试获取依赖Bean
Constructor<?> constructorToUse = determineConstructor(beanName, mbd);
// 若依赖的Bean正在创建中(即循环依赖),立即抛出异常
if (mbd.isPrototype() && mbd.getDependentBeans().contains(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
return instantiateBean(beanName, mbd); // 此处触发异常
}
异常信息示例
Error creating bean with name 'beanA':
Requested bean is currently in creation: Is there an unresolvable circular reference?
规避方案
- 改用 Setter/Field 注入
@Component public class BeanA { private BeanB beanB; @Autowired // 改为属性注入 public void setBeanB(BeanB beanB) { this.beanB = beanB; } }
- 延迟注入(
@Lazy
)@Component public class BeanA { private final BeanB beanB; public BeanA(@Lazy BeanB beanB) { // 延迟代理 this.beanB = beanB; } }
- 原理:创建代理对象而非真实 Bean,打破实例化死锁
- 风险:可能将异常延迟到运行时
二、多例(Prototype)作用域的循环依赖:❌ 完全不支持
核心原因
- 无缓存机制:Prototype Bean 每次
getBean()
都生成新对象 - 三级缓存失效:
singletonObjects
/earlySingletonObjects
/singletonFactories
仅缓存单例 Bean
源码逻辑(AbstractBeanFactory
)
if (mbd.isPrototype()) {
if (isPrototypeCurrentlyInCreation(beanName)) {
// 发现当前线程已在创建该Prototype Bean → 循环依赖
throw new BeanCurrentlyInCreationException(beanName);
}
beforePrototypeCreation(beanName); // 标记创建状态
prototypeInstance = createBean(beanName, mbd, args);
}
关键限制
prototypesCurrentlyInCreation
线程变量仅用于检测循环依赖,而非解决
典型报错
Error creating bean with name 'beanA':
Scope 'prototype' is not active for the current thread;
Consider defining a scoped proxy for this bean if you intend to refer to it from a singleton;
三、对比总结:Spring 解决循环依赖的能力边界
依赖类型 | 是否支持解决 | 原因 |
---|---|---|
单例 + Setter/Field注入 | ✅ | 三级缓存可提前暴露半成品对象 |
单例 + 构造器注入 | ❌ | 对象未实例化无法暴露引用 |
多例(任何注入方式) | ❌ | 无对象缓存机制,每次创建都是独立流程 |
四、终极解决方案
1. 代码重构(治本之策)
- 引入中间层
@Component public class ServiceBridge { @Autowired private BeanA beanA; @Autowired private BeanB beanB; }
- 事件驱动解耦
// BeanA 发布事件 applicationContext.publishEvent(new EventA()); // BeanB 监听事件 @EventListener public void handleEvent(EventA event) {...}
2. 使用 Provider 延迟获取(推荐)
@Component
public class BeanA {
private final Provider<BeanB> beanBProvider; // javax.inject.Provider
public BeanA(Provider<BeanB> beanBProvider) {
this.beanBProvider = beanBProvider;
}
public void execute() {
BeanB beanB = beanBProvider.get(); // 使用时才创建
}
}
3. 方法注入(仅适用原型)
@Component
public abstract class BeanA {
public void process() {
BeanB beanB = createBeanB();
}
@Lookup // Spring会动态生成子类实现此方法
protected abstract BeanB createBeanB();
}
五、设计启示
避免循环依赖是首要原则:
- 70% 可通过调整代码结构避免
- 20% 通过延迟加载解决
- 10% 考虑架构重构
慎用构造器注入场景:
- 强依赖关系(如数据库连接池)→ 适合构造器注入
- 弱依赖关系(如工具类)→ 优先Setter注入
原型Bean使用规范:
- 永远不注入单例Bean(会导致状态污染)
- 用
ObjectFactory
或Provider
封装获取逻辑