Spring 使用三级缓存(而不是两级)来解决循环依赖,主要目的是兼容 AOP(动态代理)场景,同时保持 Bean 创建过程的语义一致性扩展性

如果只用两级缓存,在大多数普通属性注入的循环依赖场景下确实可以工作,但一旦引入 AOP(@Transactional、@Async、@Cacheable 等),就会出现非常严重的问题提前暴露的对象和最终容器里的对象不是同一个(类型/引用不一致),导致运行时类型转换异常、代理失效、事务不生效等。

三级缓存各自职责(DefaultSingletonBeanRegistry)

缓存层级 Map 类型 存放的内容 什么时候放入 什么时候取出并移除 核心目的
一级缓存 singletonObjects 普通 bean(成品) 完全初始化好的 bean(可能已代理) 初始化完成、放入成品后 几乎所有 getBean 最终都走这里 成品缓存,正常使用的地方
二级缓存 earlySingletonObjects 早期普通对象(半成品) 已实例化但还没完成属性填充和初始化的原始对象 从三级缓存拿到并暴露早期引用后 被其他 bean 依赖时取出,移到一级 防止重复创建同一个早期对象
三级缓存 singletonFactories ObjectFactory(lambda 工厂) 生成早期引用的工厂(通常是 getEarlyBeanReference 的 lambda) put 时创建 bean 实例后立即放入 第一次被循环依赖时调用 getObject() → 生成早期对象(或代理对象)→ 放入二级 延迟决定是否要 AOP 代理 的关键点

为什么二级缓存不够?(最核心原因)

假设我们只有一级 + 二级(成品 + 早期普通对象):

A → B → A  (A 被 AOP 代理)
  1. 创建 A → 实例化(new A) → 放入二级缓存(早期 A,原始对象)
  2. A 需要注入 B → 创建 B
  3. B 需要注入 A → 从二级缓存拿到原始 A(还没代理)
  4. B 完成 → 放入一级缓存
  5. A 继续 → 填充属性、初始化、AOP 后生成代理对象 proxyA
  6. proxyA 放入一级缓存

问题来了

  • B 手里拿到的 A 是原始 A
  • 容器最终暴露的是 proxyA
  • B 拿到的引用和最终容器里的 bean 不是同一个对象!

这会导致:

  • B 调用 A 的方法时绕过了代理 → 事务/日志/权限等切面全部失效
  • 类型检查失败(有些地方强转成代理接口会报错)
  • equals()/hashCode() 异常行为

三级缓存如何解决这个问题?

关键就在第三级:它放的不是对象本身,而是一个 ObjectFactory(lambda),这个 lambda 会在真正被别人依赖的时候才执行:

// 大致伪代码
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    return getSingleton(beanName, () -> {
        // 这里才是真正决定要不要代理的地方
        return applyBeanPostProcessorsBeforeInitialization(bean, beanName);  // → post-processor 可能返回代理对象
    });
}

流程变成:

  1. 创建 A → 实例化 → 放入三级缓存(一个 lambda:getEarlyBeanReference)
  2. A 需要 B → 创建 B
  3. B 需要 A → 从三级缓存拿到 lambda → 执行 lambda → 得到早期引用(如果是 AOP 场景,这里就会提前生成代理对象
  4. 把这个早期引用(可能是代理)放入二级缓存,同时从三级移除
  5. B 拿到的是已经代理好的 A(proxyA)
  6. A 继续后续流程,最终 proxyA 放入一级缓存

结果:B 拿到的 A 和最终容器里的 A 是同一个对象(都是 proxyA),语义一致。

总结一句话

Spring 用三级缓存而不是两级,核心是为了在提前暴露引用时仍然能正确应用 AOP 动态代理,保证“别人提前拿到的引用”和“最终容器里的 bean”是同一个对象。

如果你的项目完全关闭 AOP(never proxy),理论上两级缓存就够了(很多手写 IoC 框架就是这么干的)。但 Spring 要做成通用框架,必须兼容 AOP,所以必须用三级。

你项目里遇到过因为循环依赖 + AOP 导致的诡异 bug 吗?或者你更倾向于“能不循环就不循环”的设计哲学?

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐