详解 Spring 循环依赖如何解决

文章目录

概要

本文系统讲解 Spring 框架中的循环依赖(Circular Dependency)问题:什么是循环依赖、Spring 能解决哪些类型的循环依赖、底层依靠三级缓存 如何打破依赖闭环,以及最容易被面试官追问的两个深水区------三级缓存中的对象工厂如何生成 AOP 代理对象「Bean 升入二级缓存、从三级缓存删除」到底做了什么。读完本文,你将能从概念到源码完整地把这条链路串起来。

循环依赖指的是两个或多个 Bean 互相持有对方的引用,形成一个闭环。在不做任何处理的情况下,A 的创建依赖 B、B 的创建又依赖 A,会陷入「先有鸡还是先有蛋」的死循环。Spring 通过「提前暴露半成品 Bean 」的设计巧妙地解决了单例 Bean 的字段/Setter 注入场景下的循环依赖。

整体架构流程

一、什么是循环依赖

最典型的形态是两个 Bean 互相注入:

java 复制代码
@Component
public class A {
    @Autowired
    private B b;   // A 需要 B
}

@Component
public class B {
    @Autowired
    private A a;   // B 又需要 A
}

二者构成一个闭环,谁都无法独立完成创建:
#mermaid-svg-IP6yqkP0cZyfHCQs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IP6yqkP0cZyfHCQs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IP6yqkP0cZyfHCQs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IP6yqkP0cZyfHCQs .error-icon{fill:#552222;}#mermaid-svg-IP6yqkP0cZyfHCQs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IP6yqkP0cZyfHCQs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IP6yqkP0cZyfHCQs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IP6yqkP0cZyfHCQs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IP6yqkP0cZyfHCQs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IP6yqkP0cZyfHCQs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IP6yqkP0cZyfHCQs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IP6yqkP0cZyfHCQs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IP6yqkP0cZyfHCQs .marker.cross{stroke:#333333;}#mermaid-svg-IP6yqkP0cZyfHCQs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IP6yqkP0cZyfHCQs p{margin:0;}#mermaid-svg-IP6yqkP0cZyfHCQs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-IP6yqkP0cZyfHCQs .cluster-label text{fill:#333;}#mermaid-svg-IP6yqkP0cZyfHCQs .cluster-label span{color:#333;}#mermaid-svg-IP6yqkP0cZyfHCQs .cluster-label span p{background-color:transparent;}#mermaid-svg-IP6yqkP0cZyfHCQs .label text,#mermaid-svg-IP6yqkP0cZyfHCQs span{fill:#333;color:#333;}#mermaid-svg-IP6yqkP0cZyfHCQs .node rect,#mermaid-svg-IP6yqkP0cZyfHCQs .node circle,#mermaid-svg-IP6yqkP0cZyfHCQs .node ellipse,#mermaid-svg-IP6yqkP0cZyfHCQs .node polygon,#mermaid-svg-IP6yqkP0cZyfHCQs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IP6yqkP0cZyfHCQs .rough-node .label text,#mermaid-svg-IP6yqkP0cZyfHCQs .node .label text,#mermaid-svg-IP6yqkP0cZyfHCQs .image-shape .label,#mermaid-svg-IP6yqkP0cZyfHCQs .icon-shape .label{text-anchor:middle;}#mermaid-svg-IP6yqkP0cZyfHCQs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IP6yqkP0cZyfHCQs .rough-node .label,#mermaid-svg-IP6yqkP0cZyfHCQs .node .label,#mermaid-svg-IP6yqkP0cZyfHCQs .image-shape .label,#mermaid-svg-IP6yqkP0cZyfHCQs .icon-shape .label{text-align:center;}#mermaid-svg-IP6yqkP0cZyfHCQs .node.clickable{cursor:pointer;}#mermaid-svg-IP6yqkP0cZyfHCQs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IP6yqkP0cZyfHCQs .arrowheadPath{fill:#333333;}#mermaid-svg-IP6yqkP0cZyfHCQs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IP6yqkP0cZyfHCQs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IP6yqkP0cZyfHCQs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IP6yqkP0cZyfHCQs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IP6yqkP0cZyfHCQs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IP6yqkP0cZyfHCQs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IP6yqkP0cZyfHCQs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IP6yqkP0cZyfHCQs .cluster text{fill:#333;}#mermaid-svg-IP6yqkP0cZyfHCQs .cluster span{color:#333;}#mermaid-svg-IP6yqkP0cZyfHCQs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IP6yqkP0cZyfHCQs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IP6yqkP0cZyfHCQs rect.text{fill:none;stroke-width:0;}#mermaid-svg-IP6yqkP0cZyfHCQs .icon-shape,#mermaid-svg-IP6yqkP0cZyfHCQs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IP6yqkP0cZyfHCQs .icon-shape p,#mermaid-svg-IP6yqkP0cZyfHCQs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IP6yqkP0cZyfHCQs .icon-shape .label rect,#mermaid-svg-IP6yqkP0cZyfHCQs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IP6yqkP0cZyfHCQs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IP6yqkP0cZyfHCQs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IP6yqkP0cZyfHCQs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} A 需要 B
B 需要 A
Bean A

持有 B 的引用
Bean B

持有 A 的引用

二、核心思路:把 Bean 的创建拆成两步

打破闭环的关键洞察是:一个 Bean 的创建可以分为两个阶段------

  1. 实例化(instantiation) :调用构造器 new 出对象,此时属性还是空的;
  2. 属性填充 + 初始化(populate + initialize):注入依赖、执行初始化逻辑。

既然实例化和属性填充是分开的,那么 A 在 new 出来之后、还没填充属性之前,就可以把这个「半成品 A」提前暴露出去。这样当 B 来索取 A 时,能先拿到这个半成品引用把自己组装完整,A 随后再继续,闭环就被打破了。

三、三级缓存结构

Spring 用三级缓存(均定义在 DefaultSingletonBeanRegistry 中)来管理「半成品暴露」的过程:

层级 字段名 存放内容 说明
一级缓存 singletonObjects 完整的成品 Bean 属性已填充、初始化已完成,可直接使用
二级缓存 earlySingletonObjects 早期半成品 Bean 已实例化、未填充属性的早期引用(含已生成的代理)
三级缓存 singletonFactories 对象工厂 ObjectFactory 决定是否提前生成 AOP 代理

查找一个 Bean 时,按 一级 → 二级 → 三级 的顺序依次查找。

四、完整解决流程(以 A 依赖 B、B 依赖 A 为例)

#mermaid-svg-McvCI4gkVr33SMLp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-McvCI4gkVr33SMLp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-McvCI4gkVr33SMLp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-McvCI4gkVr33SMLp .error-icon{fill:#552222;}#mermaid-svg-McvCI4gkVr33SMLp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-McvCI4gkVr33SMLp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-McvCI4gkVr33SMLp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-McvCI4gkVr33SMLp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-McvCI4gkVr33SMLp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-McvCI4gkVr33SMLp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-McvCI4gkVr33SMLp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-McvCI4gkVr33SMLp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-McvCI4gkVr33SMLp .marker.cross{stroke:#333333;}#mermaid-svg-McvCI4gkVr33SMLp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-McvCI4gkVr33SMLp p{margin:0;}#mermaid-svg-McvCI4gkVr33SMLp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-McvCI4gkVr33SMLp .cluster-label text{fill:#333;}#mermaid-svg-McvCI4gkVr33SMLp .cluster-label span{color:#333;}#mermaid-svg-McvCI4gkVr33SMLp .cluster-label span p{background-color:transparent;}#mermaid-svg-McvCI4gkVr33SMLp .label text,#mermaid-svg-McvCI4gkVr33SMLp span{fill:#333;color:#333;}#mermaid-svg-McvCI4gkVr33SMLp .node rect,#mermaid-svg-McvCI4gkVr33SMLp .node circle,#mermaid-svg-McvCI4gkVr33SMLp .node ellipse,#mermaid-svg-McvCI4gkVr33SMLp .node polygon,#mermaid-svg-McvCI4gkVr33SMLp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-McvCI4gkVr33SMLp .rough-node .label text,#mermaid-svg-McvCI4gkVr33SMLp .node .label text,#mermaid-svg-McvCI4gkVr33SMLp .image-shape .label,#mermaid-svg-McvCI4gkVr33SMLp .icon-shape .label{text-anchor:middle;}#mermaid-svg-McvCI4gkVr33SMLp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-McvCI4gkVr33SMLp .rough-node .label,#mermaid-svg-McvCI4gkVr33SMLp .node .label,#mermaid-svg-McvCI4gkVr33SMLp .image-shape .label,#mermaid-svg-McvCI4gkVr33SMLp .icon-shape .label{text-align:center;}#mermaid-svg-McvCI4gkVr33SMLp .node.clickable{cursor:pointer;}#mermaid-svg-McvCI4gkVr33SMLp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-McvCI4gkVr33SMLp .arrowheadPath{fill:#333333;}#mermaid-svg-McvCI4gkVr33SMLp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-McvCI4gkVr33SMLp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-McvCI4gkVr33SMLp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-McvCI4gkVr33SMLp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-McvCI4gkVr33SMLp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-McvCI4gkVr33SMLp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-McvCI4gkVr33SMLp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-McvCI4gkVr33SMLp .cluster text{fill:#333;}#mermaid-svg-McvCI4gkVr33SMLp .cluster span{color:#333;}#mermaid-svg-McvCI4gkVr33SMLp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-McvCI4gkVr33SMLp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-McvCI4gkVr33SMLp rect.text{fill:none;stroke-width:0;}#mermaid-svg-McvCI4gkVr33SMLp .icon-shape,#mermaid-svg-McvCI4gkVr33SMLp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-McvCI4gkVr33SMLp .icon-shape p,#mermaid-svg-McvCI4gkVr33SMLp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-McvCI4gkVr33SMLp .icon-shape .label rect,#mermaid-svg-McvCI4gkVr33SMLp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-McvCI4gkVr33SMLp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-McvCI4gkVr33SMLp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-McvCI4gkVr33SMLp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ① 实例化 A(属性为空)

A 的工厂放入三级缓存
② 填充 A 属性,发现需要 B

暂停 A,转去创建 B
③ 实例化 B(属性为空)

B 的工厂放入三级缓存
④ 填充 B 属性,发现需要 A

查缓存:一二级未命中,三级命中
⑤ 调用 A 的工厂,得到 A 的早期引用

A 升入二级缓存,三级缓存删除
⑥ B 拿到 A 的引用 → B 初始化完成

B 成为成品,放入一级缓存
⑦ 回到 A,拿到完整的 B → A 初始化完成

A 成为成品,放入一级缓存,闭环打破

整个流程最关键的是第 ⑤ 步 :B 缺 A 时并不是重新创建一个 A(那样又会循环),而是从三级缓存拿到 A 的早期引用(这里后面会再次解释),先用它完成自身组装。

技术名词解释

  • 循环依赖(Circular Dependency):两个或多个 Bean 互相持有对方引用形成的依赖闭环。
  • 一级缓存 singletonObjects:存放完整可用的单例成品 Bean。
  • 二级缓存 earlySingletonObjects :存放提前暴露的早期半成品 Bean(已实例化、属性未填充,可能是已生成好的代理对象)。
  • 三级缓存 singletonFactories :存放生成早期引用的对象工厂 ObjectFactory
  • ObjectFactory:一个函数式接口,三级缓存中存放的工厂本质是一段延迟执行的 lambda,被调用时才生成早期引用。
  • getEarlyBeanReference:获取 Bean 早期引用的方法,是三级缓存工厂内部真正执行的逻辑,负责把原始对象交给后置处理器处理。
  • SmartInstantiationAwareBeanPostProcessor :智能实例化感知后置处理器,定义了 getEarlyBeanReference 扩展点。
  • AbstractAutoProxyCreator:AOP 的自动代理后置处理器,是上述扩展点在 AOP 场景下的关键实现。
  • wrapIfNecessary:真正决定「是否生成代理」并创建代理对象的方法;命中切面则返回代理,否则返回原始对象。
  • BeanCurrentlyInCreationException:当循环依赖无法解决(如构造器注入)时抛出的异常。

技术细节

一、循环依赖的三种情况(能否解决)

并不是所有循环依赖 Spring 都能解决,需要分情况讨论:

注入方式 / 作用域 能否解决 原因
单例 + 字段(@Autowired)/Setter 注入 ✅ 能 可以先暴露半成品,打破闭环
单例 + 构造器注入 ❌ 不能 实例化阶段就要拿到对方,没机会暴露半成品
原型(prototype)作用域 ❌ 不能 不走缓存、每次新建,无法提前暴露

构造器注入为什么不行 :三级缓存的工厂是在 createBeanInstance(实例化)之后 才放入缓存的。而构造器注入要求在调用构造器、也就是实例化的那一刻 就拿到依赖对象,此时对方的半成品根本还没来得及暴露,于是直接抛出 BeanCurrentlyInCreationException

二、三级缓存中的工厂如何生成代理对象

一个常见误解是:以为代理对象在「放入三级缓存」时就生成好了。实际上代理是在工厂被调用(getObject())的那一刻才生成的,这正是三级缓存「延迟」设计的精髓。

工厂在 doCreateBean 中被注册进三级缓存,本质是一段 lambda:

java 复制代码
// AbstractAutowireCapableBeanFactory#doCreateBean
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
//                             ↑ 这个 lambda 就是三级缓存里存的"工厂"

此时 getEarlyBeanReference 一行都还没执行,只有当别的 Bean 来索取它、触发 getObject() 时才真正运行。运行时的调用链如下:
#mermaid-svg-5GCNTe23OfxAfQzW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5GCNTe23OfxAfQzW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5GCNTe23OfxAfQzW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5GCNTe23OfxAfQzW .error-icon{fill:#552222;}#mermaid-svg-5GCNTe23OfxAfQzW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5GCNTe23OfxAfQzW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5GCNTe23OfxAfQzW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5GCNTe23OfxAfQzW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5GCNTe23OfxAfQzW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5GCNTe23OfxAfQzW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5GCNTe23OfxAfQzW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5GCNTe23OfxAfQzW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5GCNTe23OfxAfQzW .marker.cross{stroke:#333333;}#mermaid-svg-5GCNTe23OfxAfQzW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5GCNTe23OfxAfQzW p{margin:0;}#mermaid-svg-5GCNTe23OfxAfQzW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5GCNTe23OfxAfQzW .cluster-label text{fill:#333;}#mermaid-svg-5GCNTe23OfxAfQzW .cluster-label span{color:#333;}#mermaid-svg-5GCNTe23OfxAfQzW .cluster-label span p{background-color:transparent;}#mermaid-svg-5GCNTe23OfxAfQzW .label text,#mermaid-svg-5GCNTe23OfxAfQzW span{fill:#333;color:#333;}#mermaid-svg-5GCNTe23OfxAfQzW .node rect,#mermaid-svg-5GCNTe23OfxAfQzW .node circle,#mermaid-svg-5GCNTe23OfxAfQzW .node ellipse,#mermaid-svg-5GCNTe23OfxAfQzW .node polygon,#mermaid-svg-5GCNTe23OfxAfQzW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5GCNTe23OfxAfQzW .rough-node .label text,#mermaid-svg-5GCNTe23OfxAfQzW .node .label text,#mermaid-svg-5GCNTe23OfxAfQzW .image-shape .label,#mermaid-svg-5GCNTe23OfxAfQzW .icon-shape .label{text-anchor:middle;}#mermaid-svg-5GCNTe23OfxAfQzW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5GCNTe23OfxAfQzW .rough-node .label,#mermaid-svg-5GCNTe23OfxAfQzW .node .label,#mermaid-svg-5GCNTe23OfxAfQzW .image-shape .label,#mermaid-svg-5GCNTe23OfxAfQzW .icon-shape .label{text-align:center;}#mermaid-svg-5GCNTe23OfxAfQzW .node.clickable{cursor:pointer;}#mermaid-svg-5GCNTe23OfxAfQzW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5GCNTe23OfxAfQzW .arrowheadPath{fill:#333333;}#mermaid-svg-5GCNTe23OfxAfQzW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5GCNTe23OfxAfQzW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5GCNTe23OfxAfQzW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5GCNTe23OfxAfQzW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5GCNTe23OfxAfQzW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5GCNTe23OfxAfQzW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5GCNTe23OfxAfQzW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5GCNTe23OfxAfQzW .cluster text{fill:#333;}#mermaid-svg-5GCNTe23OfxAfQzW .cluster span{color:#333;}#mermaid-svg-5GCNTe23OfxAfQzW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5GCNTe23OfxAfQzW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5GCNTe23OfxAfQzW rect.text{fill:none;stroke-width:0;}#mermaid-svg-5GCNTe23OfxAfQzW .icon-shape,#mermaid-svg-5GCNTe23OfxAfQzW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5GCNTe23OfxAfQzW .icon-shape p,#mermaid-svg-5GCNTe23OfxAfQzW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5GCNTe23OfxAfQzW .icon-shape .label rect,#mermaid-svg-5GCNTe23OfxAfQzW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5GCNTe23OfxAfQzW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5GCNTe23OfxAfQzW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5GCNTe23OfxAfQzW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 命中切面,如 @Transactional
未命中,普通 Bean
factory.getObject()

B 来要 A,三级缓存的工厂被调用
getEarlyBeanReference()

遍历所有后置处理器
AbstractAutoProxyCreator

AOP 自动代理后置处理器
wrapIfNecessary()

判断该 Bean 是否命中切面
返回代理对象
返回原始对象

两段关键源码:

java 复制代码
// AbstractAutowireCapableBeanFactory#getEarlyBeanReference
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;   // 默认是原始对象
    // 遍历 SmartInstantiationAwareBeanPostProcessor
    for (SmartInstantiationAwareBeanPostProcessor bp : ...) {
        exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
    }
    return exposedObject;   // 可能已被替换为代理对象
}
java 复制代码
// AbstractAutoProxyCreator#getEarlyBeanReference(AOP 的关键实现)
public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);   // 登记:这个 Bean 提前代理过
    return wrapIfNecessary(bean, beanName, cacheKey); // 真正生成代理的地方
}

代理生成的真正动作落在 wrapIfNecessary------它与 Bean 正常初始化后生成代理用的是同一个方法,只是循环依赖让它提前触发了。

其中 earlyProxyReferences.put(...) 这行很关键:它做了一次登记。等 A 后续走到正常的初始化后置处理阶段时,自动代理器会先查这张登记表,发现该 Bean 已经提前代理过,就不会再代理第二遍,从而避免「代理套代理」。

三、「A 升入二级缓存,三级缓存删除」具体做了什么

这一步发生在查缓存的方法 getSingleton 中。当三级缓存命中时,连续做三个动作:

java 复制代码
// DefaultSingletonBeanRegistry#getSingleton(关键三行)
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
    singletonObject = singletonFactory.getObject();           // ① 调工厂,生成早期引用(可能是代理)
    this.earlySingletonObjects.put(beanName, singletonObject); // ② 放入二级缓存
    this.singletonFactories.remove(beanName);                 // ③ 从三级缓存移除
}

用大白话翻译:

  • 调用工厂:执行上一节的调用链,得到 A 的早期引用(若 A 命中切面,这里得到的就是 A 的代理)。
  • 升入二级缓存 :把这个刚生成好的对象 存进二级缓存 earlySingletonObjects
  • 三级缓存删除 :把 A 对应的工厂 lambda 从三级缓存 singletonFactories 中删掉,因为它的使命已经完成。

四、为什么必须做这个「升级」动作(二级缓存的存在意义)

考虑一个场景:B 和 C 都依赖 A ,且 A 命中切面需要生成代理。工厂里的 wrapIfNecessary 每调一次就会造一个新代理
#mermaid-svg-kUgRrEMJFn4Zr9gI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kUgRrEMJFn4Zr9gI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kUgRrEMJFn4Zr9gI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kUgRrEMJFn4Zr9gI .error-icon{fill:#552222;}#mermaid-svg-kUgRrEMJFn4Zr9gI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kUgRrEMJFn4Zr9gI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kUgRrEMJFn4Zr9gI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kUgRrEMJFn4Zr9gI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kUgRrEMJFn4Zr9gI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kUgRrEMJFn4Zr9gI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kUgRrEMJFn4Zr9gI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kUgRrEMJFn4Zr9gI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kUgRrEMJFn4Zr9gI .marker.cross{stroke:#333333;}#mermaid-svg-kUgRrEMJFn4Zr9gI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kUgRrEMJFn4Zr9gI p{margin:0;}#mermaid-svg-kUgRrEMJFn4Zr9gI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kUgRrEMJFn4Zr9gI .cluster-label text{fill:#333;}#mermaid-svg-kUgRrEMJFn4Zr9gI .cluster-label span{color:#333;}#mermaid-svg-kUgRrEMJFn4Zr9gI .cluster-label span p{background-color:transparent;}#mermaid-svg-kUgRrEMJFn4Zr9gI .label text,#mermaid-svg-kUgRrEMJFn4Zr9gI span{fill:#333;color:#333;}#mermaid-svg-kUgRrEMJFn4Zr9gI .node rect,#mermaid-svg-kUgRrEMJFn4Zr9gI .node circle,#mermaid-svg-kUgRrEMJFn4Zr9gI .node ellipse,#mermaid-svg-kUgRrEMJFn4Zr9gI .node polygon,#mermaid-svg-kUgRrEMJFn4Zr9gI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kUgRrEMJFn4Zr9gI .rough-node .label text,#mermaid-svg-kUgRrEMJFn4Zr9gI .node .label text,#mermaid-svg-kUgRrEMJFn4Zr9gI .image-shape .label,#mermaid-svg-kUgRrEMJFn4Zr9gI .icon-shape .label{text-anchor:middle;}#mermaid-svg-kUgRrEMJFn4Zr9gI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kUgRrEMJFn4Zr9gI .rough-node .label,#mermaid-svg-kUgRrEMJFn4Zr9gI .node .label,#mermaid-svg-kUgRrEMJFn4Zr9gI .image-shape .label,#mermaid-svg-kUgRrEMJFn4Zr9gI .icon-shape .label{text-align:center;}#mermaid-svg-kUgRrEMJFn4Zr9gI .node.clickable{cursor:pointer;}#mermaid-svg-kUgRrEMJFn4Zr9gI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kUgRrEMJFn4Zr9gI .arrowheadPath{fill:#333333;}#mermaid-svg-kUgRrEMJFn4Zr9gI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kUgRrEMJFn4Zr9gI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kUgRrEMJFn4Zr9gI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kUgRrEMJFn4Zr9gI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kUgRrEMJFn4Zr9gI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kUgRrEMJFn4Zr9gI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kUgRrEMJFn4Zr9gI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kUgRrEMJFn4Zr9gI .cluster text{fill:#333;}#mermaid-svg-kUgRrEMJFn4Zr9gI .cluster span{color:#333;}#mermaid-svg-kUgRrEMJFn4Zr9gI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kUgRrEMJFn4Zr9gI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kUgRrEMJFn4Zr9gI rect.text{fill:none;stroke-width:0;}#mermaid-svg-kUgRrEMJFn4Zr9gI .icon-shape,#mermaid-svg-kUgRrEMJFn4Zr9gI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kUgRrEMJFn4Zr9gI .icon-shape p,#mermaid-svg-kUgRrEMJFn4Zr9gI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kUgRrEMJFn4Zr9gI .icon-shape .label rect,#mermaid-svg-kUgRrEMJFn4Zr9gI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kUgRrEMJFn4Zr9gI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kUgRrEMJFn4Zr9gI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kUgRrEMJFn4Zr9gI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 有二级缓存(正确)
B 要 A
代理 A

工厂只跑一次
C 要 A
都拿到同一个代理

单例保证 ✓
没有二级缓存(错误)
B 要 A
代理 A#1

工厂第一次跑
C 要 A
代理 A#2

工厂第二次跑
两个不同代理

单例被破坏 ✗

如果不缓存工厂结果,B 调一次得到「代理 A#1」、C 再调一次得到「代理 A#2」,容器里就出现了两个不同的 A,单例彻底崩坏。

所以二级缓存的本质是「工厂结果的缓存」 :第一次有人来要 A 时,调工厂生成代理并存入二级缓存;之后再有谁来要 A,直接从二级缓存复用同一个对象,不再调工厂。这样无论多少个 Bean 依赖 A,大家拿到的都是同一个代理实例,单例语义得以保证。同时三级缓存的工厂已无用处,顺手删掉,避免被再次误调。

五、为什么需要三级缓存而不是两级

结论:如果不考虑 AOP,两级缓存就够了;三级缓存的存在是为了正确处理 AOP 代理。

  • 正常情况下,AOP 代理是在 Bean 初始化之后由后置处理器生成的。
  • 但循环依赖时,依赖方会提前拿走目标对象的引用,此时它还没初始化、还没生成代理。
  • 三级缓存存的不是对象而是工厂 ,把「要不要提前生成代理」这个判断延迟到真正发生循环依赖、有人来索取的那一刻才执行。
  • 若直接用二级缓存存代理对象,就意味着每个 Bean 无论是否发生循环依赖都要在实例化后强行生成代理,破坏了 Spring「代理应在初始化后阶段完成」的生命周期设计。

一句话:第三级缓存(工厂)的作用,是把「是否提前生成 AOP 代理」的判断延迟到真正需要的那一刻,不打扰没有循环依赖的普通 Bean。

六、一个完整 Bean 在三级缓存中的生命周期

  • A 实例化后 → 工厂进入三级缓存。
  • 第一个依赖 A 的 Bean 来索取 → 调工厂生成(代理)→ 结果进入二级缓存,三级缓存删除。
  • 后续依赖 A 的 Bean → 直接从二级缓存复用同一个对象。
  • A 自己最终初始化完成 → 放入一级 缓存,同时把二、三级缓存中 A 的残留一次性清除(addSingleton 中统一 remove)。

至此,A 在三个缓存中的旅程走完:三级 → 二级 → 一级,每升一级都意味着它更「成熟」一步。

小结

Spring 通过「提前暴露半成品」配合三级缓存,优雅地解决了单例 + 字段/Setter 注入场景下的循环依赖问题,核心要点可归纳为:

  • 解决范围:仅能解决单例的字段/Setter 注入循环依赖;构造器注入与 prototype 作用域无法解决。
  • 三级缓存职责:一级存成品、二级存半成品、三级存对象工厂,三者各司其职。
  • 工厂生成代理 :工厂包裹 getEarlyBeanReference,被调用时委托 wrapIfNecessary 判断是否命中切面,命中即当场生成代理------代理是「被调用那一刻」才产生的。
  • 升二级、删三级:调一次工厂拿到早期引用后存入二级、删除三级,本质是缓存工厂结果,保证工厂只跑一次、所有依赖方拿到同一个对象,守住单例。
  • 三级缓存的根本原因:为了配合 AOP,把是否提前代理的判断延迟到真正需要时,不破坏 Bean 的正常生命周期。
  • 工程建议 :循环依赖往往是设计耦合过重的信号,可优先使用构造器注入(让问题在启动时即暴露),或通过 @Lazy、职责拆分、改用 Setter 注入等方式从根上规避。