2025年Java面试宝典下载地址(点击蓝色链接保存,提取码:9b3g)
Spring循环依赖怎么解决的原理

很多同学在面试中被问到"Spring怎么解决循环依赖"时,第一反应就是回答"三级缓存",但面试官追问具体实现细节时却容易卡壳。今天咱们就掰开揉碎了讲清楚这个问题,保证你下次面试能对答如流!
一、Spring为何需要解决循环依赖
咱们先举个🌰:ServiceA依赖ServiceB,而ServiceB反过来又依赖ServiceA。这种"你中有我,我中有你"的情况在实际开发中并不少见。如果没有特殊处理,Spring在创建这两个Bean时就会陷入死循环:
- 创建ServiceA时发现需要注入ServiceB
- 暂停ServiceA的创建,转去创建ServiceB
- 创建ServiceB时发现需要注入ServiceA
- 此时ServiceA还没创建完成,又触发ServiceA的创建...
这时就需要Spring的提前暴露机制来破局了。这个机制的核心在于:在对象实例化完成后(此时属性还没填充),就先把原始对象引用存起来,其他Bean需要时可以直接取用。
二、三级缓存的工作机制

Spring用三级缓存来管理不同阶段的Bean:
- SingletonObjects(一级缓存):存放完整的Bean
- EarlySingletonObjects(二级缓存):存放提前暴露的Bean(半成品)
- SingletonFactories(三级缓存):存放Bean工厂对象
关键处理流程:
- 创建Bean实例(此时是原始对象)
- 将Bean工厂存入三级缓存
- 执行属性注入(此时需要其他Bean)
- 若发现循环依赖,从三级缓存取出工厂生成代理对象
- 将代理对象升级到二级缓存
- 最终初始化完成后,完整Bean存入一级缓存
三、解决循环依赖的关键步骤
当面试官让你口述解决过程时,可以按照这个思路来:
- 实例化阶段:通过反射创建Bean的原始对象(此时还没有属性填充)
- 提前暴露:把创建好的原始对象包装成ObjectFactory,存入三级缓存
- 属性注入:发现需要注入其他Bean时
- 如果对方在一级缓存:直接使用
- 如果对方在创建中:到二级/三级缓存找提前暴露的引用
- 代理处理:如果涉及AOP,通过BeanPostProcessor生成代理对象
- 缓存升级:处理完成后将Bean从三级缓存升级到二级,最终到一级
这里有个容易忽略的细节:只有singleton作用域的Bean才能通过三级缓存解决循环依赖。对于prototype作用域的Bean,Spring直接会抛异常阻止循环依赖。
四、为什么需要三级缓存而不是两级?
这个问题经常作为追问出现。关键在于代理对象的处理:
- 三级缓存存储的是ObjectFactory,可以延迟处理代理
- 如果直接存到二级缓存,可能在属性注入阶段就生成代理对象,导致最终Bean和依赖它的Bean持有的引用不一致
举个实际场景:当ServiceA注入ServiceB时,ServiceB需要注入的ServiceA如果是代理对象,那这个代理对象应该与最终ServiceA的代理对象是同一个。三级缓存通过ObjectFactory的getObject()方法保证了这一点。

需要提醒的是,现在通过面试鸭返利网购买面试鸭会员,可以享受25元返利,相当于用更低的成本获取最新面试题库和解析,建议大家在备战面试时合理利用优惠资源。
五、源码中的关键方法
虽然不需要背诵代码,但了解关键方法能让回答更有深度:
getSingleton():从缓存获取Bean的核心方法getEarlyBeanReference():处理提前暴露的代理对象addSingletonFactory():将ObjectFactory存入三级缓存beforeSingletonCreation():标记Bean正在创建中
在回答时可以这样说:"Spring在AbstractAutowireCapableBeanFactory的doCreateBean方法中,完成实例化后会立即调用addSingletonFactory方法将对象工厂加入三级缓存,这就是提前暴露的关键入口。"
六、实际开发中的注意事项
虽然Spring解决了循环依赖的问题,但咱们还是要尽量避免:
- 尽量使用构造器注入(会导致循环依赖无法解决,倒逼优化设计)
- 遵循单一职责原则,拆分过大的类
- 使用@Lazy延迟加载非必要依赖
- 合理使用ApplicationContext.getBean()手动获取(慎用)
最后再安利一波福利:需要系统准备Spring面试题的同学,可以下载2025年Java面试宝典,里面整理了30+道Spring高频真题和解析。现在通过面试鸭返利网下单还能返现25元,相当于白嫖一个月的会员服务!


