Spring 循环依赖解决方案完全指南
一、什么是循环依赖
循环依赖指的是两个或多个 Bean 之间相互依赖,形成了一个闭环。例如:
- A 依赖 B,B 依赖 A
- A 依赖 B,B 依赖 C,C 依赖 A
二、Spring 解决方案的核心要素
1. 三级缓存
// 一级缓存:存储完全初始化的 bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存:存储早期的 bean 引用
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// 三级缓存:存储 bean 的工厂对象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
2. 创建中标记集合
// 记录正在创建中的 bean
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
三、解决循环依赖的完整流程
1. Bean 创建开始
protected void beforeSingletonCreation(String beanName) {
// 将 bean 加入创建中标记集合
if (!this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
2. 实例化 Bean
- 创建空对象(仅调用构造方法)
- 将对象工厂放入三级缓存
3. 属性注入时发现循环依赖
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 先查一级缓存
Object singletonObject = this.singletonObjects.get(beanName);
// 如果一级缓存没有,且当前 bean 正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 查二级缓存
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果二级缓存也没有,尝试三级缓存
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> factory = this.singletonFactories.get(beanName);
if (factory != null) {
// 从三级缓存获取早期引用
singletonObject = factory.getObject();
// 放入二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 从三级缓存移除
this.singletonFactories.remove(beanName);
}
}
}
return singletonObject;
}
四、循环依赖的判定过程
1. 标记判定
- 通过
singletonsCurrentlyInCreation
集合判断 Bean 是否在创建中 - 当发现要获取的 Bean 在创建中时,说明可能存在循环依赖
2. 处理流程
- A 创建时,将 A 加入
singletonsCurrentlyInCreation
- A 注入属性时发现依赖 B,开始创建 B
- B 创建时,将 B 加入
singletonsCurrentlyInCreation
- B 注入属性时发现依赖 A
- 发现 A 在
singletonsCurrentlyInCreation
中,确认存在循环依赖 - 尝试从三级缓存获取 A 的早期引用
五、缓存的移除时机
1. Bean 完全初始化后
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 放入一级缓存
this.singletonObjects.put(beanName, singletonObject);
// 从三级缓存移除
this.singletonFactories.remove(beanName);
// 从二级缓存移除
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
2. Bean 创建失败时
protected void removeSingleton(String beanName) {
synchronized (this.singletonObjects) {
this.singletonObjects.remove(beanName);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.remove(beanName);
}
}
六、关键点总结
1. singletonsCurrentlyInCreation
的作用
- 是循环依赖检测的核心
- 记录 Bean 的创建状态
- 用于及时发现循环依赖问题
2. 三级缓存的配合
- 三级缓存存储工厂对象,支持 AOP 代理
- 二级缓存存储早期引用,避免重复创建
- 一级缓存存储完整对象,最终使用
3. 局限性
- 只能解决 setter 注入的循环依赖
- 只能解决单例 Bean 的循环依赖
- 不能解决构造器注入的循环依赖
七、最佳实践
- 尽量避免循环依赖的设计
- 使用
@Lazy
注解处理特殊场景 - 优先使用构造器注入,更容易发现循环依赖问题
- 合理设计类的依赖关系,避免复杂的依赖环