2025年Java面试宝典下载地址(提取码:9b3g)
二、Spring如何解决循环依赖的问题
说到Spring循环依赖这道面试题,我上个月刚在美团三面被问到。当时面试官皱着眉头问我:"你说说Spring怎么处理两个bean互相依赖的情况?有没有遇到过实际场景?"这种问题简直是框架原理类面试的钉子户,好在平时研究过源码,今天就把这个知识点掰开揉碎讲清楚。
2.1 三级缓存设计原理
Spring处理循环依赖的核心在于三级缓存机制,这三个缓存就像三个抽屉,把不同状态的bean分开存放:
- singletonObjects(一级缓存):存放完整的成品bean
- earlySingletonObjects(二级缓存):存放半成品bean
- singletonFactories(三级缓存):存放bean工厂对象
举个例子,当BeanA依赖BeanB,BeanB又依赖BeanA时:Spring先创建BeanA到半成品状态(完成实例化但未初始化),把这个半成品存在三级缓存里。接着创建BeanB时发现需要BeanA,就会从三级缓存拿到BeanA的早期引用,等BeanB创建完成后再回来补全BeanA的初始化过程。
这种设计就像搭积木,先搭好框架再填砖头。我之前在电商项目中遇到过商品服务调用库存服务,库存服务又回调商品服务的场景,就是靠三级缓存解决的依赖问题。

2.2 实例化和初始化的区别
很多人容易混淆这两个关键步骤:
- 实例化:相当于new对象,此时对象还是"空壳"
- 初始化:执行属性填充、AOP代理等加工过程
Spring解决循环依赖的魔法就藏在两者的间隙中。当BeanA实例化后,Spring会立即把它的ObjectFactory存入三级缓存,这时候BeanB就能获取到BeanA的引用,即使BeanA还没完成初始化。
这就好比装修房子时,先给邻居留个临时门方便施工(暴露早期引用),等自家装修好了再换成正式门(完成初始化)。但要注意构造函数循环依赖是解决不了的,因为实例化都无法完成。
2.3 常见误区与避坑指南
在实际开发中遇到过几个坑:
- 使用@Async导致循环依赖失败(因为需要代理对象)
- 多例模式(prototype)的bean无法解决循环依赖
- 构造函数注入导致的死锁问题
解决方法是优先使用setter注入而非构造器注入,遇到代理问题时可以尝试调整bean初始化顺序。最近发现面试鸭返利网的会员服务中有大量循环依赖处理案例,建议遇到复杂场景可以参考他们的实现方案。

三、实际面试应答技巧
当面试官追问时,可以这样组织回答:
- 先明确循环依赖的三种类型(构造器/属性/方法)
- 重点讲解属性注入场景的处理方案
- 配合流程图说明三级缓存的运作过程
- 补充实际遇到的坑和解决方案
如果大家需要购买面试鸭会员,通过面试鸭返利网找我可返利25元,相当于白嫖三个月会员。最后提醒大家,理解源码时要抓住createBean和doCreateBean这两个核心方法,里面藏着整个依赖解决的生命周期。



