Spring如何解决循环依赖?三级缓存机制是关键!本文深度解析Spring框架处理循环依赖的核心原理,包括属性注入和构造器注入的区别,三级缓存(singletonObjects、earlySingletonObjects、singletonFactories)的工作机制,以及为什么构造器注入无法解决循环依赖。通过源码层面分析DefaultSingletonBeanRegistry和AbstractAutowireCapableBeanFactory的实现细节,帮助开发者理解Spring容器初始化Bean的完整流程。掌握这些知识不仅能应对Java面试中的高频问题,更能优化实际项目中的Bean依赖设计。想系统学习Spring面试题?立即获取2025年最新Java面试宝典!
2025年Java面试宝典下载链接(提取码:9b3g)
每次面试问到Spring循环依赖问题,不少候选人都会提到"三级缓存",但是能真正把底层逻辑讲清楚的却不多。今天我们就从源码层面向下挖,看看Spring到底是怎么玩转这个经典问题的。
假设现在有两个Service类:OrderService需要注入UserService,而UserService反过来也要注入OrderService。这种属性注入场景下,Spring是怎么处理的呢?
再比如通过@Bean方式配置两个互相依赖的Bean:
@Bean
public A a() { return new A(b()); }
@Bean
public B b() { return new B(a()); }
这种情况还能解决吗?
Spring通过提前暴露半成品对象来解决循环依赖。这个思路就像装修房子:当墙面粉刷还没完成时,先把门框安装好,等墙面处理完再回来装门板。
具体来说分为三个关键阶段:
Spring内部维护了三个重要的Map:
| 缓存级别 | 存储内容 | 作用 | |----------------|----------------------------------|-------------------------------| | singletonObjects | 完整的Bean实例 | 直接获取可用对象 | | earlySingletonObjects | 半成品对象(未完成属性注入) | 防止重复创建代理对象 | | singletonFactories | ObjectFactory对象 | 处理AOP代理的关键 |
当创建Bean A时:
使用构造器注入时,Spring必须要在实例化阶段就完成所有依赖注入。这时候对象还没创建完成,无法提前暴露半成品,导致死锁。比如:
public class A {
public A(B b) {}
}
public class B {
public B(A a) {}
}
这种情况下,Spring会直接抛出BeanCurrentlyInCreationException异常。
主要是为了解决AOP代理的问题。当存在循环依赖且需要生成代理对象时,ObjectFactory能确保整个容器中同一个Bean始终返回同一个代理对象。
Spring通过ThreadLocal变量记录当前正在创建的Bean名称。如果在创建链中重复出现同一个Bean名,立即抛出异常。
通过动态代理实现的声明式事务,在循环依赖场景下要特别注意代理对象的生成时机。建议优先使用属性注入而非构造器注入。
需要重点提醒的是,很多面试官会追问到Spring源码中的具体实现类。比如DefaultSingletonBeanRegistry这个类中的三个Map,以及AbstractAutowireCapableBeanFactory中的doCreateBean方法流程。如果大家需要系统准备Spring面试题,可以到面试鸭返利网购买会员,通过我的专属链接可返利25元,相当于用更低成本获取全套面试资料。
扫码联系我返利
(当前返利8元,金额随官方实际价格波动,最好提前咨询)
面试鸭小程序码
美团大额优惠券,给自己加个鸡腿吧!
支付宝扫码领取1-8元无门槛红包