Spring三级缓存解密:循环依赖破局之道
一、循环依赖的致命困境
1.1 典型场景示例
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
传统初始化流程:
- 创建ServiceA实例 → 需要注入ServiceB
- 创建ServiceB实例 → 需要注入ServiceA
- 回到步骤1 → 无限循环
二、三级缓存架构揭秘
2.1 缓存层次结构
缓存层级 | 存储内容 | 生命周期阶段 |
---|---|---|
一级缓存 | 完全初始化好的单例Bean | 初始化完成 |
二级缓存 | 完成实例化但未初始化的原始Bean | 属性填充前 |
三级缓存 | Bean的ObjectFactory(工厂对象) | 实例化后立即存入 |
三、缓存操作核心流程
3.1 Bean创建流程图解
四、源码级执行分析
4.1 关键方法调用链
// AbstractBeanFactory.doGetBean()
↓ getSingleton(beanName, true) // 尝试从缓存获取
↓ getSingleton(beanName, () -> createBean(...))
↓ createBean()
↓ doCreateBean()
↓ addSingletonFactory(beanName, () -> getEarlyBeanReference(...)) // 存入三级缓存
↓ populateBean() // 属性注入触发依赖查找
4.2 缓存交互时序
// 首次获取ServiceA
1. getSingleton("serviceA") → null
2. createBean() → 实例化ServiceA
3. addSingletonFactory("serviceA", ObjectFactory)
4. populateBean() → 发现需要serviceB
// 获取ServiceB
5. getSingleton("serviceB") → null
6. createBean() → 实例化ServiceB
7. addSingletonFactory("serviceB", ObjectFactory)
8. populateBean() → 发现需要serviceA
// 再次获取ServiceA
9. getSingleton("serviceA") → 从三级缓存获取ObjectFactory
10. getEarlyBeanReference() → 返回原始对象或代理对象
11. 将serviceA引用存入二级缓存
五、AOP代理的特殊处理
5.1 代理对象生成时机
// AbstractAutowireCapableBeanFactory
protected Object getEarlyBeanReference(String beanName,
RootBeanDefinition mbd,
Object bean) {
// 如果存在SmartInstantiationAwareBeanPostProcessor
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp =
(SmartInstantiationAwareBeanPostProcessor) bp;
return ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
关键点:
- 三级缓存存储的是ObjectFactory而非原始对象
- AOP代理在此阶段提前生成
- 保证最终注入的是同一个代理对象
六、缓存策略的局限性
6.1 无法解决的场景
场景类型 | 示例 | 解决方案 |
---|---|---|
构造器循环依赖 | new ServiceA(serviceB) | 使用@Lazy延迟注入 |
prototype作用域 | @Scope("prototype") | 重构代码设计 |
异步初始化 | @Async初始化方法 | 调整初始化顺序 |
非单例Bean | 非Spring管理的对象 | 保证依赖项为单例 |
6.2 典型异常分析
// 构造器注入导致的异常信息
BeanCurrentlyInCreationException:
Error creating bean with name 'serviceA':
Requested bean is currently in creation:
Is there an unresolvable circular reference?
破局方案:
// 使用@Lazy注解打破循环
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
七、生产环境调优建议
7.1 缓存配置参数
# 允许早期引用暴露(默认true)
spring.main.allow-circular-references=true
# 单例缓存最大数量(默认无限制)
spring.beans.singleton.max-count=10000
7.2 性能监控指标
// 监控缓存命中率
DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) ctx.getBeanFactory();
int singletonCount = registry.getSingletonCount();
int earlySingletonCount = registry.getEarlySingletonReferences().size();
八、现代架构演进
8.1 与Spring Boot的整合
8.2 响应式编程的影响
// Reactor上下文中的延迟注入
@Bean
public ServiceA serviceA(ObjectProvider<ServiceB> serviceBProvider) {
return new ServiceA(serviceBProvider.getIfAvailable());
}