Spring 的三级缓存是解决单例Bean循环依赖的核心机制。理解三级缓存对于掌握Spring的Bean创建过程至关重要。
一、三级缓存定义与作用
三级缓存的含义
// 在 DefaultSingletonBeanRegistry 中定义 public class DefaultSingletonBeanRegistry extends ... { // 一级缓存:存放完全初始化好的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); }各级缓存的作用
| 缓存级别 | 名称 | 存储内容 | 作用 |
|---|---|---|---|
| 一级缓存 | singletonObjects | 完全初始化好的单例Bean | 缓存最终可用的Bean |
| 二级缓存 | earlySingletonObjects | 早期Bean(半成品) | 解决循环依赖,避免重复创建代理 |
| 三级缓存 | singletonFactories | ObjectFactory(Bean工厂) | 创建早期引用,支持AOP等扩展 |
二、循环依赖场景分析
场景一:最简单的循环依赖
@Component public class A { @Autowired private B b; } @Component public class B { @Autowired private A a; }场景二:自我依赖(少见但能说明问题)
@Component public class SelfRefBean { @Autowired private SelfRefBean self; }三、三级缓存解决循环依赖的完整流程
步骤详解(以A↔B循环依赖为例)
// 模拟三级缓存解决循环依赖的过程 public class ThreeLevelCacheDemo { public static void main(String[] args) { // 创建过程模拟 createBeanA(); } static void createBeanA() { System.out.println("1. 开始创建Bean A"); // Step 1: 实例化A(调用构造函数) Object a = new A(); System.out.println("2. A实例化完成,将A的ObjectFactory放入三级缓存"); // 三级缓存:singletonFactories.put("a", () -> getEarlyBeanReference("a", a)); // Step 2: 属性填充,发现需要B System.out.println("3. 开始填充A的属性,发现需要B"); // Step 3: 创建B createBeanB(); // Step 6: 完成A的初始化 System.out.println("9. B创建完成,继续完成A的初始化"); System.out.println("10. 将A从二级缓存移除,放入一级缓存"); // 一级缓存:singletonObjects.put("a", a); // 二级缓存:earlySingletonObjects.remove("a"); } static void createBeanB() { System.out.println("4. 开始创建Bean B"); // Step 3: 实例化B Object b = new B(); System.out.println("5. B实例化完成,将B的ObjectFactory放入三级缓存"); // 三级缓存:singletonFactories.put("b", () -> getEarlyBeanReference("b", b)); // Step 4: 属性填充,发现需要A System.out.println("6. 开始填充B的属性,发现需要A"); // Step 5: 获取A(从三级缓存获取早期引用) System.out.println("7. 从三级缓存获取A的早期引用"); // Object earlyA = singletonFactories.get("a").getObject(); System.out.println("8. 将A放入二级缓存,从三级缓存移除A的工厂"); // 二级缓存:earlySingletonObjects.put("a", earlyA); // 三级缓存:singletonFactories.remove("a"); // 将earlyA注入到B System.out.println("9. 将A的早期引用注入B,完成B的初始化"); System.out.println("10. 将B从二级缓存移除,放入一级缓存"); // 一级缓存:singletonObjects.put("b", b); // 二级缓存:earlySingletonObjects.remove("b"); } }流程图解
创建A → 实例化A → 将A工厂放入三级缓存 ↓ 填充A属性 → 需要B → 创建B ↓ 实例化B → 将B工厂放入三级缓存 ↓ 填充B属性 → 需要A → 从三级缓存获取A工厂 ↓ ↓ 创建A早期引用 ← 执行A工厂getObject() ↓ 将A早期引用放入二级缓存,移除三级缓存中的A工厂 ↓ 将A早期引用注入B → 完成B初始化 ↓ 将B放入一级缓存 → 返回B ↓ 获取到B → 注入B到A → 完成A初始化 ↓ 将A放入一级缓存,移除二级缓存中的A四、三级缓存源码深度解析
1. 获取Bean的核心方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 1. 先从一级缓存获取 Object singletonObject = this.singletonObjects.get(beanName); // 如果一级缓存没有,且Bean正在创建中(解决循环依赖) if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 2. 从二级缓存获取 singletonObject = this.earlySingletonObjects.get(beanName); // 如果二级缓存没有,且允许早期引用 if (singletonObject == null && allowEarlyReference) { // 3. 从三级缓存获取Bean工厂 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 通过工厂创建早期Bean singletonObject = singletonFactory.getObject(); // 放入二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); // 移除三级缓存中的工厂 this.singletonFactories.remove(beanName); } } } } return singletonObject; }2. Bean创建过程中的缓存操作
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) { // 1. 实例化Bean BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args); Object bean = instanceWrapper.getWrappedInstance(); // 2. 判断是否支持早期暴露(单例、允许循环引用) boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // 3. 将Bean工厂添加到三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // 4. 属性填充(可能触发循环依赖) populateBean(beanName, mbd, instanceWrapper); // 5. 初始化 Object exposedObject = initializeBean(beanName, exposedObject, mbd); // 6. 处理早期引用 if (earlySingletonExposure) { // 从一级缓存获取(检查是否已被其他Bean初始化过程修改) Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { // 如果exposedObject没有被增强,使用早期引用 if (exposedObject == bean) { exposedObject = earlySingletonReference; } } } 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); // 记录已注册的单例 this.registeredSingletons.add(beanName); } } }3. 获取早期Bean引用(支持AOP)
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; // 1. 如果Bean需要被后处理器增强(如AOP) if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; // 2. 获取早期引用(可能是代理对象) exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; }五、为什么需要三级缓存?二级不够吗?
场景分析:AOP代理的循环依赖
@Component public class A { @Autowired private B b; public void doSomething() { System.out.println("A do something"); } } @Component public class B { @Autowired private A a; // 这里期望注入的是A的代理对象,而不是原始对象 public void test() { // 如果注入的是原始对象,AOP增强会失效 a.doSomething(); } } @Aspect @Component public class LogAspect { @Before("execution(* com.example.A.doSomething(..))") public void logBefore() { System.out.println("Log before method execution"); } }三级缓存的必要性
延迟代理创建:三级缓存存储的是
ObjectFactory,可以延迟决定何时以及如何创建代理保证代理一致性:确保所有Bean注入的是同一个代理实例
避免重复创建代理:如果没有三级缓存,每次获取早期引用都可能创建新的代理
如果只有二级缓存的问题
// 假设只有二级缓存 public Object getSingleton(String beanName) { Object bean = singletonObjects.get(beanName); if (bean == null && isSingletonCurrentlyInCreation(beanName)) { // 只有二级缓存:直接创建早期引用 bean = createEarlyBeanReference(beanName); earlySingletonObjects.put(beanName, bean); } return bean; } // 问题:如果多个地方同时获取早期引用,可能创建多个不同的代理实例 // 特别是当A需要被AOP增强时六、特殊场景处理
1. 构造器循环依赖(无法解决)
@Component public class ConstructorA { private ConstructorB b; @Autowired public ConstructorA(ConstructorB b) { // 构造器注入 this.b = b; } } @Component public class ConstructorB { private ConstructorA a; @Autowired public ConstructorB(ConstructorA a) { // 构造器注入 this.a = a; } } // 抛出 BeanCurrentlyInCreationException原因:构造器注入时,Bean还未实例化,无法放入三级缓存。
2. 原型Bean的循环依赖(无法解决)
@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class PrototypeA { @Autowired private PrototypeB b; } @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTotype) public class PrototypeB { @Autowired private PrototypeA a; } // 抛出 BeanCurrentlyInCreationException原因:原型Bean不缓存,每次都是新创建,Spring不支持原型Bean的循环依赖。
3. @Async注解的循环依赖
@Component public class AsyncA { @Autowired private AsyncB b; @Async public void asyncMethod() { // 异步方法 } } @Component public class AsyncB { @Autowired private AsyncA a; // 这里注入的可能是原始对象,而不是代理 }解决方法:使用@Lazy注解
@Component public class AsyncB { @Lazy @Autowired private AsyncA a; }七、三级缓存与AOP的协作
代理创建时机
// AbstractAutoProxyCreator(AOP核心类)中的处理 public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); // 将原始Bean和代理关系缓存起来 this.earlyProxyReferences.put(cacheKey, bean); // 创建代理 return wrapIfNecessary(bean, beanName, cacheKey); } // 后续初始化完成后检查 public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); // 如果早期已经创建过代理,直接返回早期代理 if (this.earlyProxyReferences.remove(cacheKey) != bean) { // 否则检查是否需要创建代理 return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }八、性能与线程安全考虑
1. 缓存访问的同步
// DefaultSingletonBeanRegistry 中的同步控制 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. 性能优化
一级缓存使用
ConcurrentHashMap,支持高并发读二级缓存使用
ConcurrentHashMap,但实际访问需要加锁三级缓存使用
HashMap,因为只在创建Bean时访问,且需要同步
九、实际调试技巧
1. 查看三级缓存状态
@SpringBootTest public class CacheDebugTest { @Autowired private ApplicationContext applicationContext; @Test public void debugThreeLevelCache() throws Exception { DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) ((AbstractApplicationContext) applicationContext).getBeanFactory(); // 通过反射查看缓存内容 Field singletonObjectsField = DefaultSingletonBeanRegistry.class .getDeclaredField("singletonObjects"); singletonObjectsField.setAccessible(true); Map<String, Object> singletonObjects = (Map<String, Object>) singletonObjectsField.get(registry); System.out.println("一级缓存大小: " + singletonObjects.size()); // 类似方式可以查看二级和三级缓存 } }2. 循环依赖调试配置
# application.properties # 开启循环依赖调试日志 logging.level.org.springframework.beans.factory.support=DEBUG # 关闭Spring的循环依赖快速失败(仅用于调试) spring.main.allow-circular-references=true十、最佳实践与注意事项
1. 避免循环依赖
优先使用构造器注入:强制在编译期发现循环依赖
代码重构:提取公共逻辑到第三个类中
使用@Lazy注解:延迟加载打破循环
2. 设计建议
// 不好的设计:双向紧密耦合 @Service public class OrderService { @Autowired private UserService userService; } @Service public class UserService { @Autowired private OrderService orderService; } // 好的设计:提取公共逻辑 @Service public class OrderService { @Autowired private CommonService commonService; } @Service public class UserService { @Autowired private CommonService commonService; } @Service public class CommonService { // 公共业务逻辑 }3. 配置建议
@Configuration public class AppConfig { // 如果不使用AOP,可以关闭循环引用支持提升性能 @Bean public static BeanFactoryPostProcessor disableCircularReferences() { return beanFactory -> { if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowCircularReferences(false); } }; } }总结
Spring三级缓存的核心价值:
解决循环依赖:通过提前暴露Bean引用
支持AOP:确保注入的是正确的代理对象
保证单例唯一性:避免重复创建Bean实例
性能优化:减少不必要的Bean创建
理解三级缓存不仅有助于解决循环依赖问题,更能深入理解Spring容器的设计哲学。在实际开发中,虽然三级缓存解决了技术问题,但良好的设计应该尽量避免循环依赖的出现。