跳至主要內容

Spring 源码学习-理解三级缓存

科哒大约 3 分钟

合图片内容和 Spring 源码的三级缓存设计,我将通过 分层解析流程推演 的方式,帮助你彻底理解 Spring 如何通过三级缓存解决循环依赖问题,并解释图中提到的“三级改二级”的可行性。


1. 三级缓存的核心设计

Spring 在 DefaultSingletonBeanRegistry 类中定义了三个关键缓存:

// 一级缓存:存放完整初始化后的单例 Bean(成品)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 二级缓存:存放提前暴露的 Bean(半成品,未完成属性注入和初始化)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

// 三级缓存:存放 Bean 的 ObjectFactory(用于处理代理对象的延迟生成)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

2. 循环依赖解决流程(以 A→B→A 为例)

步骤 1:创建 Bean A

  1. 实例化 A:通过反射调用构造器创建对象 A@1234(此时属性未填充)。
  2. 暴露 ObjectFactory:将 A 的 ObjectFactory 存入三级缓存(图中提到的 addSingletonFactory 方法)。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

步骤 2:填充 A 的属性时发现依赖 B

  1. 触发 B 的创建:调用 getBean("B"),进入 B 的创建流程。
  2. 实例化 B:创建对象 B@5678,并暴露 B 的 ObjectFactory 到三级缓存。

步骤 3:填充 B 的属性时再次请求 A

  1. 从三级缓存获取 A:调用 getSingleton("A"),通过三级缓存中的 ObjectFactory 生成 A 的早期引用(可能触发 AOP 代理)。
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
    // 调用 getEarlyBeanReference 生成代理对象(如果需要)
    earlySingletonObjects.put(beanName, singletonFactory.getObject());
    singletonFactories.remove(beanName);
}
  1. 将 A 的早期引用存入二级缓存:此时 earlySingletonObjects 中存有 A@Proxy(代理对象)。

步骤 4:完成 B 的创建

  1. B 初始化完成:B 成为完整对象,存入一级缓存 singletonObjects

步骤 5:回到 A 的创建流程

  1. 继续填充 A 的属性:注入已初始化的 B。
  2. A 初始化完成:存入一级缓存,删除二级缓存中的早期引用。

3. 为何需要三级缓存?(关键难点)

  • 代理对象的延迟生成:如果 A 需要 AOP 代理,三级缓存的 ObjectFactory 能延迟生成代理对象,直到真正发生循环依赖时才创建(避免无意义的代理)。
  • 二级缓存直接暴露半成品的风险:若只有二级缓存,可能返回未经代理的原始对象(破坏 AOP 特性)。

4. 图中提到的“三级改二级”是否可行?

方法 1:修改 doCreateBean 中的代码

  • 直接暴露半成品到二级缓存
// 原代码:addSingletonFactory(三级缓存)
// 修改为:
earlySingletonObjects.put(beanName, bean);
  • 风险:如果 Bean 需要代理,会导致后续注入的是原始对象而非代理对象。

方法 2:移除三级缓存,合并到二级缓存

  • 调整 getSingleton 逻辑
// 原三级缓存逻辑
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
    object = singletonFactory.getObject();
    this.earlySingletonObjects.put(beanName, object);
    this.singletonFactories.remove(beanName);
}

// 改为直接从二级缓存获取
object = this.earlySingletonObjects.get(beanName);
  • 代价:必须提前生成代理对象(无论是否发生循环依赖),牺牲了按需代理的灵活性。

5. 从图片文字看 Spring 源码的关键点

  • getEarlyBeanReference** 方法**:生成早期引用,处理 AOP 代理(见 AbstractAutowireCapableBeanFactory)。
  • doCreateBean** 中的 addSingletonFactory**:暴露 ObjectFactory 到三级缓存的入口。
  • 循环依赖的闭环逻辑:通过缓存层级跳转避免无限递归(如 A→B→A 的依赖链)。

6. 总结:三级缓存的核心价值

缓存层级作用设计意义
一级缓存(成品)存放完整 Bean保证单例完整性
二级缓存(半成品)存放提前暴露的 Bean(可能代理)快速响应循环依赖请求
三级缓存(工厂)延迟生成代理对象按需代理,避免无效对象创建

通过三级缓存的协作,Spring 在性能(减少代理生成)、正确性(AOP 兼容)和扩展性(支持多种后处理器)之间取得了平衡。