一次并不简单的 Spring 循环依赖排查

一、问题背景

在一个 Spring Boot 项目中,我为系统接入了多个业务入口 ,这些入口都会依赖一个统一的服务类(AgentService),用于对外调用 AI 服务。

在功能逐步拓展后,项目启动时报错,核心异常如下:

复制代码
BeanCurrentlyInCreationException:
Bean has been injected in its raw version as part of a circular reference,
but has eventually been wrapped

初看像是一个普通的循环依赖问题,但随着排查深入,发现事情并不简单。


二、第一反应:多个类依赖同一个 Bean,真的会有问题吗?

在项目中:

  • 多个业务 Service 都依赖同一个 AgentService

  • 这些 Service 之间并没有直接互相注入

这类结构在 Spring 中是完全合法的,而且之前也一直运行正常。

所以第一时间就能判断:
问题不在"多个类依赖同一个 Bean"本身


三、从异常链路入手,定位真正的依赖路径

我开始认真分析 Spring 启动时给出的 依赖创建链路,发现了一个非常关键的信息:

复制代码
AgentService 被注入到多个业务 Service 中,
但在创建过程中最终被"包裹(wrapped)"了

这说明两点:

  1. Spring 在创建 AgentService 时,中途就被其他 Bean 使用了

  2. AgentService 最终需要生成代理对象(AOP)

这已经不是"普通字段注入"的问题了。


四、关键突破口:为什么 Spring 一定要"最终成品"?

Spring 的循环依赖在很多情况下是可以解决的,这是大家都知道的事实。

那为什么这次不行?

原因在于:

👉 被代理的 Bean(如 @Async、@Transactional)不能随便用半成品

而我在 AgentService 中发现了这样一个方法:

复制代码
@Async
public void someAsyncMethod(...) {
    ...
}

这意味着:

  • Spring 必须为该 Bean 创建 AOP 代理

  • 代理只能作用在 最终完成的 Bean 上

  • 一旦别的 Bean 在此之前拿到了"原始对象",Spring 就会直接报错


五、验证假设:最小成本试验

为了验证是否真的是 @Async 导致的问题,我并没有立即重构代码,而是先做了一件事:

临时注释掉 @Async 注解

这是一个非常低成本、可回滚的试验。

结果:

✅ 应用顺利启动,循环依赖错误消失

这一步,基本可以确认根因。


六、问题本质总结

这次问题并不是:

  • 多个平台接入

  • 多个类依赖同一个 Service

  • WebClient 或远程调用问题

而是一个典型但容易被忽略的 Spring 行为差异:

普通 Bean 可以使用"半成品"解决循环依赖
但需要 AOP 代理的 Bean(如 @Async)必须是最终成品

一旦这两种需求同时存在,Spring 就只能选择 失败而不是冒险。


七、经验与反思

通过这次排查,我有几点比较深刻的收获:

  1. 不要急着"拆依赖"

    • 先判断 Spring 为什么不帮你解决
  2. 异常信息本身就包含答案

    • 特别是 raw version、wrapped 这类关键词
  3. AOP 是循环依赖的高危放大器

    • @Async、@Transactional 都需要格外注意
  4. 验证假设,比"拍脑袋改代码"更重要


八、结语

Spring 的循环依赖问题,从来不是"会不会",而是:

"在什么前提下,Spring愿不愿意帮你兜底"

相关推荐
Leo.yuan4 分钟前
2026企业级数据集成平台市场报告:国内外厂商格局与技术路线
数据库·oracle
霸道流氓气质5 分钟前
AI应用成本优化完全指南:Token压缩、语义缓存与模型路由的Java生产级实战
java·人工智能·缓存
mmm.c13 分钟前
Redis 两级缓存 + Pub/Sub 广播:一次为抗高并发做的缓存改造
java
yexianglunbai14 分钟前
中间件(Middleware)详解:概念、作用与实战
java·开发语言·中间件
叶总没有会15 分钟前
OpenFeign
java·开发语言·springcloud·openfeign
小七在进步16 分钟前
类和对象(四)
java·javascript·ajax
guo_wen_qiang19 分钟前
云服务器mysql分库分表环境搭建(2库4表)
数据库·mysql·docker
白帽攻防录19 分钟前
SRC 挖洞:Apache Tomcat 加密拦截器绕过深度复盘,CVE-2026-34486 fail-open 一行代码怎么打穿集群通信
java·网络安全·tomcat·apache
jianpeng的工程笔记21 分钟前
EL8 / EL9 安装 Slurm 26.05.4:主备控制器、MariaDB 记账与作业验收
linux·数据库·mariadb·高性能计算·slurm
后端LV23 分钟前
三级缓存的两个失效盲区,我用 binlog 和消费组广播补上了
java·架构