Spring 通过 三级缓存 解决单例 Bean 的循环依赖问题,核心思想是 提前暴露未完全初始化的 Bean 引用。以下是三级缓存的工作原理和完整处理流程:
1. 三级缓存定义
请结合Spring Bean 的生命周期详解理解。
缓存名称 | 存储内容 | 作用阶段 |
---|---|---|
singletonFactories | 存放 Bean 工厂对象(ObjectFactory ),用于生成未完全初始化的 Bean | Bean 实例化后,属性注入前 |
earlySingletonObjects | 存放 提前暴露的 Bean 实例(已实例化但未完成属性注入) | 属性注入阶段,解决循环依赖时使用 |
singletonObjects | 存放 完全初始化后的单例 Bean(已完成属性注入和初始化) | Bean 完全初始化后 |
2. 循环依赖处理流程
以 ServiceA
依赖 ServiceB
,同时 ServiceB
依赖 ServiceA
为例:
步骤 1:实例化 ServiceA
// 1. 反射创建 ServiceA 实例(此时未注入属性)
ServiceA serviceA = new ServiceA();
// 2. 将 ServiceA 的 ObjectFactory 存入 singletonFactories
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, bean));
-
singletonFactories
内容:{ "serviceA": ObjectFactory<ServiceA> }
步骤 2:ServiceA
属性注入(发现依赖 Serv
iceB
)
// 1. 发现需要注入 ServiceB
if (dependency instanceof ServiceB) {
// 2. 尝试从容器获取 ServiceB(触发 ServiceB 的创建)
ServiceB serviceB = getBean("serviceB");
}
步骤 3:实例化 ServiceB
// 1. 反射创建 ServiceB 实例
ServiceB serviceB = new ServiceB();
// 2. 将 ServiceB 的 ObjectFactory 存入 singletonFactories
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, bean));
-
singletonFactories
内容:{ "serviceA": ObjectFactory<ServiceA>, "serviceB": ObjectFactory<ServiceB> }
步骤 4:ServiceB
属性注入(发现依赖 ServiceA
)
// 1. 发现需要注入 ServiceA
if (dependency instanceof ServiceA) {
// 2. 尝试从容器获取 ServiceA
ServiceA serviceA = getBean("serviceA");
}
此时 Spring 的处理逻辑:
-
检查
singletonObjects
:无ServiceA
(未完全初始化)。 -
检查
earlySingletonObjects
:无ServiceA
。 -
从
singletonFactories
获取ObjectFactory
:ObjectFactory<?> singletonFactory = singletonFactories.get("serviceA"); ServiceA serviceA = (ServiceA) singletonFactory.getObject();
-
将
ServiceA
存入earlySingletonObjects
(标记为提前暴露):earlySingletonObjects.put("serviceA", serviceA); singletonFactories.remove("serviceA");
-
缓存状态
singletonObjects: {"serviceB": ServiceB@5678} earlySingletonObjects: {"serviceA": ServiceA@1234}
步骤 5:完成 ServiceB
的初始化
// 1. 将 ServiceA 的引用注入 ServiceB
serviceB.setServiceA(serviceA);
// 2. 执行 ServiceB 的初始化(@PostConstruct等)
initBean(serviceB);
// 3. 将完全初始化的 ServiceB 存入 singletonObjects
singletonObjects.put("serviceB", serviceB);
singletonFactories.remove("serviceB");
-
缓存状态:
singletonObjects: {"serviceB": ServiceB@5678} earlySingletonObjects: {"serviceA": ServiceA@1234}
步骤 6:完成 ServiceA
的初始化
// 1. 将 ServiceB 的引用注入 ServiceA
serviceA.setServiceB(serviceB);
// 2. 执行 ServiceA 的初始化(@PostConstruct等)
initBean(serviceA);
// 3. 将完全初始化的 ServiceA 存入 singletonObjects
singletonObjects.put("serviceA", serviceA);
earlySingletonObjects.remove("serviceA");
-
最终缓存状态:
singletonObjects: {"serviceA": ServiceA@1234, "serviceB": ServiceB@5678} earlySingletonObjects: {} singletonFactories: {}
3. 关键代码逻辑(简化版)
// AbstractAutowireCapableBeanFactory
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
// 1. 实例化 Bean
Object bean = createBeanInstance(beanName, mbd, args);
// 2. 将 Bean 的 ObjectFactory 存入 singletonFactories(三级缓存)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// 3. 属性注入(可能触发循环依赖)
populateBean(beanName, mbd, instanceWrapper);
// 4. 初始化 Bean(@PostConstruct等)
initializeBean(beanName, exposedObject, mbd);
return exposedObject;
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
}
}
}
4. 为什么需要三级缓存?
缓存层级 | 解决的核心问题 |
---|---|
singletonFactories | 存储生成代理对象的逻辑(如 AOP),确保循环依赖时返回的是最终代理对象,而非原始对象。 |
earlySingletonObjects | 避免重复执行 ObjectFactory.getObject() (如多次 AOP 代理)。 |
singletonObjects | 存储完全初始化后的 Bean,避免重复创建。 |
经典场景:
如果 Bean 需要 AOP 代理,singletonFactories
会存储生成代理的逻辑,确保循环依赖时注入的是代理对象,而非原始对象。
5. 循环依赖的支持限制
依赖类型 | 是否支持 | 原因 |
---|---|---|
构造器循环依赖 | ❌ 不支持 | 实例化前无法提前暴露引用(未进入三级缓存) |
Setter/字段循环依赖 | ✅ 支持 | 实例化后可通过三级缓存暴露半成品 Bean |
原型(Prototype)循环依赖 | ❌ 不支持 | Spring 不缓存原型 Bean,无法提前暴露引用 |
6. 如何避免循环依赖?
-
设计层面:
-
避免双向依赖,改用单向依赖。
-
使用
@Lazy
延迟加载。
-
-
代码层面:
-
用构造器注入 +
@Autowired(required=false)
。 -
提取公共逻辑到第三方类。
-
7. 总结
Spring 的三级缓存机制通过 提前暴露半成品 Bean 解决了单例模式的循环依赖问题:
-
singletonFactories
:存储 Bean 工厂,用于处理代理对象 -
earlySingletonObjects
:缓存提前暴露的 Bean,避免重复创建。 -
singletonObjects
:存储最终可用的 Bean。