Spring 中如何解决循环依赖问题?构造器注入可以循环依赖吗?
·
Spring 循环依赖问题
什么是循环依赖
循环依赖是指两个或多个 Bean 相互依赖,形成闭环:
┌─────────┐ ┌─────────┐
│ Bean A │────▶│ Bean B │
└────┬────┘ └────┬────┘
│ │
└───────┬───────┘
▼
┌─────────┐
│ Bean C │
└────┬────┘
│
└──────┐
▼
┌─────────┐
│ Bean A │ ← 形成闭环
└─────────┘
Spring 解决循环依赖的机制
核心原理:三级缓存
Spring 使用三级缓存来解决单例 Bean 的循环依赖问题:
/** 一级缓存:存放完全初始化的 Bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 二级缓存:存放提前暴露的 Bean(半成品) */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/** 三级缓存:存放 Bean 工厂,用于生成提前暴露的 Bean */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
三级缓存工作流程
创建 Bean A
│
▼
实例化 A(调用构造器)
│
▼
暴露半成品 A 到三级缓存(singletonFactories)
│
▼
填充属性 A → 需要 Bean B
│
▼
创建 Bean B
│
▼
实例化 B
│
▼
暴露半成品 B 到三级缓存
│
▼
填充属性 B → 需要 Bean A
│
▼
从三级缓存获取 A → 移到二级缓存(earlySingletonObjects)
│
▼
B 初始化完成 → 移到一级缓存(singletonObjects)
│
▼
A 获取到 B,继续初始化
│
▼
A 初始化完成 → 移到一级缓存
│
▼
循环依赖解决!
代码示例
@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;
}
}
Spring 处理过程:
// 伪代码:Spring 创建 Bean 的核心逻辑
protected Object getSingleton(String beanName) {
// 1. 从一级缓存获取(完全初始化的 Bean)
Object singletonObject = singletonObjects.get(beanName);
if (singletonObject == null) {
// 2. 从二级缓存获取(提前暴露的 Bean)
singletonObject = earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 3. 从三级缓存获取(Bean 工厂)
ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
if (singletonFactory != null) {
// 调用工厂方法获取 Bean(可能返回代理对象)
singletonObject = singletonFactory.getObject();
// 移到二级缓存
earlySingletonObjects.put(beanName, singletonObject);
singletonFactories.remove(beanName);
}
}
}
return singletonObject;
}
构造器注入可以循环依赖吗?
❌ 不可以!
构造器注入无法解决循环依赖,会抛出异常:
BeanCurrentlyInCreationException: Error creating bean with name 'serviceA':
Requested bean is currently in creation: Is there an unresolvable circular reference?
原因分析
创建 Bean A
│
▼
调用构造器 A(ServiceB) → 需要 Bean B
│
▼
创建 Bean B
│
▼
调用构造器 B(ServiceA) → 需要 Bean A
│
▼
A 还在创建中,无法提供实例 → 💥 异常!
构造器注入要求所有依赖在实例化时就绪,而此时 A 还未完成实例化,无法提供给 B。
解决方案
1. 使用 Setter 注入或字段注入 ✅
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private ServiceA serviceA;
@Autowired
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
2. 使用 @Lazy 注解 ✅
@Service
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB; // 注入代理对象,真正使用时才创建
}
}
@Service
public class ServiceB {
private final ServiceA serviceA;
@Autowired
public ServiceB(@Lazy ServiceA serviceA) {
this.serviceA = serviceA;
}
}
3. 使用 @PostConstruct 初始化 ✅
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
@PostConstruct
public void init() {
// 在这里使用 serviceB
}
}
4. 重构设计,消除循环依赖 ✅(推荐)
// 提取公共接口
public interface CommonService {
void doSomething();
}
@Service
public class ServiceA implements CommonService {
private final CommonService commonService;
@Autowired
public ServiceA(CommonService commonService) {
this.commonService = commonService;
}
}
@Service
public class ServiceB implements CommonService {
private final CommonService commonService;
@Autowired
public ServiceB(CommonService commonService) {
this.commonService = commonService;
}
}
循环依赖解决能力对比
| 注入方式 | 能否解决循环依赖 | 原因 |
|---|---|---|
| 构造器注入 | ❌ 不能 | 实例化时就需要依赖对象 |
| Setter 注入 | ✅ 能 | 实例化后再注入依赖 |
| 字段注入 | ✅ 能 | 实例化后再注入依赖 |
| @Lazy | ✅ 能 | 延迟加载,注入代理对象 |
注意事项
⚠️ 原型 Bean 不支持循环依赖
@Scope("prototype")
@Service
public class PrototypeA {
@Autowired
private PrototypeB prototypeB;
}
@Scope("prototype")
@Service
public class PrototypeB {
@Autowired
private PrototypeA prototypeA;
}
原型 Bean 每次都创建新实例,Spring 不会缓存,无法解决循环依赖。
⚠️ @Async 导致的循环依赖
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
@Async
public void asyncMethod() {
serviceB.doSomething();
}
}
@Async 会生成代理对象,可能导致循环依赖问题,需要使用 @Lazy 或重构。
总结
| 问题 | 答案 |
|---|---|
| Spring 如何解决循环依赖? | 三级缓存机制 |
| 构造器注入可以循环依赖吗? | ❌ 不可以 |
| Setter/字段注入可以吗? | ✅ 可以 |
| 最佳解决方案? | 重构设计,消除循环依赖 |
推荐做法:优先使用构造器注入,遇到循环依赖时使用 @Lazy 或重构设计。
更多推荐

所有评论(0)