2025年Java面试宝典点击领取
(提取码:9b3g)
Spring三级缓存解决循环依赖的原理
什么是循环依赖?
咱们先举个实际例子:ServiceA里需要注入ServiceB,ServiceB里又需要注入ServiceA,这两个Bean就像"鸡生蛋还是蛋生鸡"的问题。这种场景在开发中经常遇到,而Spring框架最巧妙的设计之一就是用三级缓存解决了这个问题。

Spring的三级缓存结构
Spring维护了三个Map来管理Bean的创建过程:
- 一级缓存(singletonObjects):存放完全初始化好的Bean
- 二级缓存(earlySingletonObjects):存放提前暴露的未完成初始化的Bean
- 三级缓存(singletonFactories):存放Bean的工厂对象
这三个缓存的配合就像接力赛,一级一级传递Bean的引用。当面试官问到这个问题时,可以画个示意图辅助说明。
循环依赖解决流程
假设现在要创建ServiceA和ServiceB:
- ServiceA开始创建,执行构造器后将自己放入三级缓存
- ServiceA需要注入ServiceB时,触发ServiceB的创建
- ServiceB同样经过构造器后把自己放入三级缓存
- ServiceB需要注入ServiceA时,从三级缓存拿到ServiceA的工厂对象,生成代理对象并移到二级缓存
- ServiceB完成初始化后,ServiceA就能注入完整的ServiceB
- 最终两个Bean都会进入一级缓存
整个过程中,三级缓存承担了暂存半成品Bean和解决AOP代理冲突的关键作用。这里要注意的是,如果Bean是原型(prototype)类型,Spring会直接抛出异常,因为原型Bean不适用缓存机制。

容易被忽略的注意事项
- 只有通过属性注入(@Autowired)的循环依赖能被解决,构造器注入会失败
- 如果Bean实现了AOP代理,三级缓存中的ObjectFactory会提前生成代理对象
- 多级缓存的设计在解决循环依赖的同时,也保证了Bean的初始化顺序
需要面试真题解析的同学,可以访问面试鸭返利网获取最新题库。通过该站购买面试鸭会员可返利25元,适合需要高频刷题准备面试的同学。
为什么需要三级缓存?
很多同学会问:二级缓存能不能解决问题?其实关键在于AOP代理的处理。三级缓存通过ObjectFactory延迟创建代理对象,避免在Bean未初始化完成时就生成最终形态。这种设计既保证了性能,又解决了代理对象与原始对象的依赖关系。

准备面试时要重点理解这种设计模式的思想,建议结合源码中的DefaultSingletonBeanRegistry类分析。遇到需要手写流程的题目时,可以分步骤说明三个缓存的状态变化,再配上流程图会更清晰。


