首页
关于这个博客
Search
1
Java 实现Google 账号单点登录(OAuth 2.0)全流程解析
1,421 阅读
2
Spring AI 无法获取大模型深度思考内容?解决方案来了
525 阅读
3
EasyExcel 实战:导出带图片的 Excel 完整方案
282 阅读
4
服务器遭遇 XMRig 挖矿程序入侵排查与清理全记录
265 阅读
5
微信小程序实现页面返回前确认弹窗:兼容左上角返回与右滑返回
219 阅读
Java 核心
框架与中间件
数据库技术
开发工具与效率
问题排查与踩坑记录
程序员成长与思考
前端
登录
Search
标签搜索
Spring AI
java虚拟机
JVM
Spring AI Alibaba
Java
保姆级教程
SpringBoot
Spring
WebFlux
MCP
大模型
Agent Skills
Nginx
Agent
Ubuntu
Mysql
Apache POI
自定义starter
Mybatis
响应式编程
Luca Ju
累计撰写
51
篇文章
累计收到
2
条评论
首页
栏目
Java 核心
框架与中间件
数据库技术
开发工具与效率
问题排查与踩坑记录
程序员成长与思考
前端
页面
关于这个博客
搜索到
1
篇与
的结果
2026-03-03
详细解析Spring如何解决循环依赖问题
在日常的Spring开发中,循环依赖是一个高频出现的问题,也是面试中的核心考点。本文将从概念定义、问题表现、核心原理到源码层面,全方位解析Spring是如何通过三级缓存机制优雅地解决单例Bean的循环依赖问题。一、什么是循环依赖?循环依赖,指的是两个或多个Bean之间互相持有对方的引用,形成闭环依赖关系。最典型的场景是Bean A依赖Bean B,同时Bean B又依赖Bean A。代码示例:@Component class A { // A依赖B @Resource private B b; } @Component class B { // B依赖A,形成循环 @Resource private A a; }在默认情况下,如果Spring不做特殊处理,项目启动时会抛出BeanCurrentlyInCreationException异常,提示存在循环依赖无法解决:二、Spring解决循环依赖的核心:三级缓存为了解决单例Bean的循环依赖问题,Spring设计了三级缓存机制,通过提前暴露半成品Bean的方式打破依赖闭环。三级缓存的定义缓存级别缓存名称作用一级缓存singletonObjects存放完全初始化完成的单例Bean(成品对象),供业务直接使用二级缓存earlySingletonObjects存放提前暴露的半成品Bean(已实例化但未完成属性填充和初始化)三级缓存singletonFactories存放ObjectFactory(对象工厂),这是一个函数式接口,仅在调用getObject()时才会创建Bean实例三、核心源码解析(基于 Spring 5.3.x)Spring处理Bean创建和循环依赖的核心逻辑集中在DefaultSingletonBeanRegistry类中,以下是关键源码及解析:public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { // 一级缓存:存放完全初始化好的单例Bean (成品) private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 三级缓存:存放Bean的工厂对象,用于创建提前暴露的Bean (半成品工厂) private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 二级缓存:存放提前暴露的Bean实例 (半成品,未完成属性填充和初始化) private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 记录当前正在创建的Bean名称,解决循环依赖的关键判断 private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16)); /** * 核心方法:获取单例Bean(解决循环依赖的入口) * @param beanName Bean名称 * @param allowEarlyReference 是否允许提前引用半成品Bean * @return 单例Bean实例 */ @Nullable public Object getSingleton(String beanName, boolean allowEarlyReference) { // 第一步:优先从一级缓存获取成品Bean Object singletonObject = this.singletonObjects.get(beanName); // 如果一级缓存没有,且当前Bean正在创建中(循环依赖的核心判断条件) if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 第二步:从二级缓存获取提前暴露的半成品Bean singletonObject = this.earlySingletonObjects.get(beanName); // 如果二级缓存也没有,且允许提前引用 if (singletonObject == null && allowEarlyReference) { // 加锁保证并发安全 synchronized (this.singletonObjects) { // 双重检查(防止多线程下重复创建) singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { // 第三步:从三级缓存获取ObjectFactory ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 通过工厂创建半成品Bean(提前暴露的核心操作) singletonObject = singletonFactory.getObject(); // 将半成品Bean放入二级缓存,同时移除三级缓存(避免重复创建) this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } } } return singletonObject; } /** * 将Bean工厂放入三级缓存(提前暴露Bean的关键步骤) * 在Bean实例化后、属性填充前调用 */ protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } } /** * 将完全初始化的Bean放入一级缓存,清理二、三级缓存 * 在Bean初始化完成后调用 */ protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); this.singletonsCurrentlyInCreation.remove(beanName); } } }调试关键方法(建议收藏)在实际调试Spring源码时,建议重点关注以下核心方法的调用链路:getBean() - Bean获取的入口方法doGetBean() - 获取Bean的核心实现createBean() - 创建Bean的顶层方法doCreateBean() - 创建Bean的核心逻辑createBeanInstance() - Bean实例化(创建空对象)populateBean() - Bean属性填充(依赖注入的核心)四、循环依赖解决完整流程(以A和B为例)结合上文A依赖B、B依赖A的场景,我们拆解Spring解决循环依赖的完整执行流程(基于单例Bean + 字段注入):步骤 1:Spring启动,开始创建Bean A调用getBean(A),首先将A标记为「正在创建中」(singletonsCurrentlyInCreation.add("A"));通过反射创建A的空实例(ctro.newInstance()),此时A的属性b为null(实例化阶段);关键操作:调用addSingletonFactory()将A的ObjectFactory放入三级缓存;开始为A填充属性,发现依赖B,触发getBean(B)。步骤 2:创建Bean B(触发循环依赖)调用getBean(B),将B标记为「正在创建中」;通过反射创建B的空实例(ctro.newInstance()),此时B的属性a为null;调用addSingletonFactory()将B的ObjectFactory放入三级缓存;开始为B填充属性,发现依赖A,再次触发getBean(A)。步骤 3:解决循环依赖(从缓存获取A)执行getBean(A),检查一级缓存:A未完成初始化,无成品;检查标记:A处于「正在创建中」,符合循环依赖条件;检查二级缓存:无A的半成品实例;检查三级缓存:存在A的ObjectFactory,调用getObject()创建A的半成品实例;将A的半成品实例从三级缓存移至二级缓存;将半成品A返回给B,完成B的属性a填充。步骤 4:B完成初始化,反馈给AB完成属性填充,执行初始化方法(init-method/@PostConstruct);调用addSingleton(B),将B放入一级缓存,并清理其二、三级缓存;将成品B返回给A,完成A的属性b填充。步骤 5:A完成初始化,最终入池A完成属性填充,执行初始化方法;调用addSingleton(A),将A放入一级缓存,清理其二、三级缓存;移除A的「正在创建中」标记,循环依赖问题解决。补充说明:加入三级缓存后的Bean创建流程可参考下图:五、关键细节:为什么需要三级缓存?核心原因是为了支持AOP动态代理:延迟创建代理对象:ObjectFactory的getObject()方法中会调用getEarlyBeanReference(),该方法会判断当前Bean是否需要生成AOP代理。只有发生循环依赖时,才会提前创建代理对象;保证代理对象的唯一性:如果没有三级缓存,所有Bean都需要提前创建代理,破坏了Spring「初始化完成后再创建代理」的设计原则;避免重复代理:三级缓存的工厂模式确保代理对象只会被创建一次,放入二级缓存后就移除三级缓存,避免重复生成。如果仅使用二级缓存,所有Bean都必须在实例化阶段就创建代理,这会导致:代理对象创建时机提前,不符合Spring的初始化生命周期无循环依赖的Bean也会被提前代理,增加不必要的性能开销总结Spring通过三级缓存机制解决单例Bean的循环依赖问题,核心是提前暴露半成品Bean打破依赖闭环;三级缓存各司其职:一级缓存存成品、二级缓存存半成品、三级缓存存工厂(支持AOP延迟代理);解决循环依赖的核心流程是:实例化Bean → 放入三级缓存 → 填充属性触发循环 → 从缓存获取半成品 → 完成初始化放入一级缓存。
2026年03月03日
16 阅读
0 评论
3 点赞