Spring 循环依赖与三级缓存 什么是循环依赖1.1 定义循环依赖是指两个及两个以上的 Bean 互相持有对方引用形成闭环依赖关系。典型场景A 依赖 B、B 依赖 A或多 Bean 闭环A→B→C→A。1.2 产生问题Spring 单例 Bean 创建核心流程先实例化原始对象 → 再填充属性依赖。在互相依赖场景下会形成递归死循环创建 A 需要先注入 B → 创建 B 需要先注入 A → 无限递归最终抛出创建异常。1.3 核心适配规则高频Spring 三级缓存仅能解决单例 Bean Setter/字段注入Autowired的循环依赖无法解决、直接抛出 BeanCurrentlyInCreationException 的场景构造方法注入实例化阶段执行还未创建三级缓存多例PrototypeBean不进入任何缓存部分工厂方法、Lookup 动态依赖场景二、单例 Bean 完整标准生命周期三级缓存原理基础Spring 单例 Bean 正常创建全流程也是循环依赖解决的核心时序依据2.1 完整执行步骤实例化 createBeanInstance通过构造方法 new 出原始裸对象仅分配内存所有属性为空。提前暴露半成品 Bean核心实例化完成、属性填充之前执行addSingletonFactory将包装了早期代理逻辑的ObjectFactory放入三级缓存。存入工厂时不会主动创建代理实现代理懒加载仅在发生循环依赖时才会触发工厂执行代理生成属性填充 populateBean解析 Autowired、Setter 方法注入所依赖的 Bean。初始化 initializeBean固定执行顺序初始化前置后置处理器postProcessBeforeInitialization自定义初始化方法PostConstruct→InitializingBean#afterPropertiesSet→ 自定义 init-method初始化后置后置处理器postProcessAfterInitialization常规 AOP 代理默认创建入口存入一级缓存将完全初始化、可对外使用的成品 Bean 放入一级缓存同时清空二、三级缓存对应记录。2.2 AOP 代理创建时机关键区别无循环依赖代理在postProcessAfterInitialization中统一创建有循环依赖代理提前通过三级缓存工厂创建后置处理器不再重复生成三、三级缓存结构、作用与互斥规则三级缓存均位于DefaultSingletonBeanRegistry核心类中三者互斥存在同一个 BeanName 不会同时存在多级缓存中。3.1 一级缓存singletonObjects存储内容完全初始化完毕的成品 Bean状态已完成实例化、属性填充、初始化、代理创建作用全局唯一对外提供可用的单例 Bean用户最终获取的 Bean 均来自一级缓存3.2 二级缓存earlySingletonObjects存储内容提前暴露的半成品 Bean原始裸对象 / 提前生成的早期 AOP 代理状态未完成属性填充、未完成初始化作用缓存已经通过三级工厂生成的早期对象避免重复调用 ObjectFactory.getObject() 重复创建代理提升性能3.3 三级缓存singletonFactories存储内容Bean 的工厂对象ObjectFactory核心逻辑工厂持有原始裸 Bean内部封装getEarlyBeanReference早期代理创建逻辑执行时机无循环依赖时工厂永久不执行仅发生循环依赖其他 Bean 需要提前获取当前 Bean 时才调用getObject()核心价值实现代理懒加载解决 AOP 场景下循环依赖产生多代理对象的问题3.4 缓存更替规则核心Bean 实例化后存入三级缓存此时一、二级缓存无数据触发循环依赖时三级工厂生成对象/代理存入二级缓存删除三级缓存记录Bean 完全初始化完成后存入一级缓存删除二、三级缓存记录3.5 循环依赖判断依据Spring 维护集合singletonsCurrentlyInCreation存储正在创建中的 BeanName。属性填充时若发现依赖的 Bean 在该集合中判定为循环依赖触发三级缓存解析逻辑。四、无 AOP 代理场景三级缓存解决循环依赖流程图源rocky-fang - Spring循环依赖文章4.1 场景单例 Bean A、B 互相字段/Setter 注入无 AOP 代理。4.2 完整执行流程创建 A实例化原始 A 对象存入三级缓存工厂属性填充阶段发现依赖 B转而创建 B。创建 B实例化原始 B 对象存入三级缓存工厂属性填充阶段发现依赖 A。容器检测到 A 处于创建中从 A 的三级缓存取出 ObjectFactory执行getObject()无代理逻辑直接返回原始 A 半成品对象。将原始 A 存入二级缓存删除 A 的三级缓存工厂完成 A 注入 B 的操作。B 完成全部属性填充、初始化流程存入一级缓存成为成品 Bean。回到 A 的创建流程注入完整的成品 BA 走完初始化流程最终存入一级缓存循环依赖解决。五、带 AOP 代理场景三级缓存核心价值重点当 Bean 需要 AOP 代理事务、切面等必须依赖三级缓存仅靠二级缓存会产生严重问题。5.1 核心 AOP 组件与方法自动代理核心类AnnotationAwareAspectJAutoProxyCreatorgetEarlyBeanReference()仅三级缓存工厂触发向earlyProxyReferences存入标记记录当前 Bean 已提前生成早期代理调用wrapIfNecessary()生成代理对象并返回postProcessAfterInitialization()常规代理创建入口读取earlyProxyReferences标记若已提前创建代理直接返回避免重复生成5.2 带 AOP 完整循环依赖流程实例化原始 A将封装代理逻辑的 ObjectFactory 存入三级缓存此时不生成代理。A 属性填充需要 B启动 B 的创建流程。实例化原始 B存入三级缓存工厂B 属性填充需要 A。容器检测 A 正在创建取出 A 的三级缓存工厂执行getObject()触发getEarlyBeanReference。打上已代理标记通过wrapIfNecessary生成早期代理对象 proxyA。proxyA 存入二级缓存删除 A 的三级缓存工厂将 proxyA 注入 B。B 完成所有创建流程存入一级缓存。回到 A注入完整成品 B执行初始化流程。执行后置处理器时检测到 A 已有代理标记不再重复创建代理。最终将二级缓存中唯一的 proxyA 存入一级缓存全局代理对象唯一。5.3 为什么不能只用二级缓存核心原理假设舍弃三级缓存实例化 Bean 后直接生成代理放入二级缓存手动调用wrapIfNecessary生成代理绕开getEarlyBeanReference不会添加代理标记。B 注入该代理对象完成依赖填充B 存入一级缓存。A 继续执行初始化后置处理检测无代理标记再次生成一个全新的代理对象。最终结果B 持有的代理、一级缓存中的代理是两个不同对象。5.4 多代理对象导致的线上问题Transactional 事务失效切面逻辑重复执行/不执行并发锁、本地缓存数据不一致对象引用不统一引发各种诡异 Bug六、终极总结背诵版循环依赖解决范围仅单例 Setter/字段注入可解构造注入、多例 Bean 直接报错。