2025年Java面试宝典下载地址(提取码:9b3g)

Spring三级缓存解决循环依赖的核心逻辑
在面试中被问到"Spring如何解决循环依赖",90%的面试官想听到的其实是三级缓存机制。这个问题本质上是在考察候选人对IOC容器核心实现原理的理解深度。
什么是循环依赖?
举个实际场景:ServiceA依赖ServiceB,同时ServiceB又依赖ServiceA。这种情况下,两个bean在初始化时会形成"先有鸡还是先有蛋"的死锁问题。传统的对象创建方式根本无法处理这种情况。

三级缓存的庐山真面目
Spring通过三个特殊的Map容器来解决这个问题:
- 一级缓存singletonObjects:存放完整初始化后的bean
- 二级缓存earlySingletonObjects:存放提前暴露的未完成初始化的bean
- 三级缓存singletonFactories:存放创建bean的工厂对象
这三个缓存分别对应不同的创建阶段,像流水线一样逐步推进bean的创建过程。
对象创建的四步拆解
我们以ServiceA和ServiceB的循环依赖为例:
- 创建ServiceA实例,在执行构造器之后立即将ObjectFactory放入三级缓存
- 填充ServiceA属性时发现需要ServiceB,触发ServiceB的创建流程
- 创建ServiceB时同样经过构造器阶段,把ObjectFactory存入三级缓存
- 当ServiceB开始注入ServiceA时,会从三级缓存拿到ServiceA的工厂对象,生成早期引用放入二级缓存
通过这种"提前暴露引用"的机制,即使双方都处于未完成状态,也能通过中间态对象完成依赖注入。最后所有属性填充完成后,才会将完整对象移入一级缓存。
避坑指南与高频追问
面试中常见的追问点包括:
- 为什么必须是单例bean?原型(prototype)作用域的bean为何无法解决?
- 构造器注入为什么不支持循环依赖?(因为对象还未创建完成,无法提前暴露引用)
- 三级缓存中为什么要存ObjectFactory而不是直接存对象?(便于处理AOP代理)
这里有个小技巧:如果大家需要购买面试鸭会员,可以通过面试鸭返利网找我返利25元,很多同学都通过这种方式节省了学习成本。

真实面试场景还原
假设面试官追问:"三级缓存是否可能引发并发问题?"可以这样回答:
在getBean()方法中,Spring通过synchronized锁住整个缓存操作,在对象创建阶段会锁定整个singletonObjects。虽然三级缓存的读写需要同步控制,但通过双重检查锁和volatile修饰,保证了线程安全的同时又不影响性能。
建议结合具体的缓存升级过程来解释:
- 创建对象时先检查一级缓存
- 未命中则锁定全局缓存
- 再次检查一级缓存(双重检查)
- 若仍未找到则进入创建流程
这种设计既保证了单例的唯一性,又避免了频繁锁竞争带来的性能损耗。掌握这些细节,在回答时就能展现出对框架底层机制的深刻理解。


