当前位置: 首页 > news >正文

如何解决 Spring Bean 循环依赖

网罗开发(小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
📣 公众号“Swift社区”,每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友“fzhanfei”,与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!


文章目录

    • 前言
    • 背景:为什么会有循环依赖?
    • Demo:复现循环依赖问题
    • 重构代码,消除循环
    • 使用 @Lazy 注解
    • 避免构造函数循环注入
    • 实际场景中的思考
    • 总结

前言

在用 Spring 开发项目时,经常会遇到一个让人头疼的问题——循环依赖
最典型的场景就是:A 注入了 BB 又注入了 A,结果 Spring 在启动的时候就直接报错了。

这种问题并不罕见,尤其在业务逻辑复杂、Bean 之间相互依赖较多的系统里,很容易就掉坑里。本文就结合一个小 Demo,带你一步步看清楚 Spring Bean 循环依赖的本质原因,并且给出几种常见的解决思路。

背景:为什么会有循环依赖?

假设有两个业务类:

  • UserService 依赖 OrderService 来查询订单。
  • OrderService 又依赖 UserService 来获取用户信息。

在代码里写出来就是这样:

@Service
public class UserService {@Autowiredprivate OrderService orderService;public void getUserInfo() {System.out.println("UserService: 获取用户信息");orderService.getOrderInfo();}
}
@Service
public class OrderService {@Autowiredprivate UserService userService;public void getOrderInfo() {System.out.println("OrderService: 获取订单信息");userService.getUserInfo();}
}

启动项目的时候,Spring 容器在实例化 UserService 时需要先实例化 OrderService
OrderService 又需要 UserService,结果就会进入死循环,最终抛出 BeanCurrentlyInCreationException

报错信息一般长这样:

Error creating bean with name 'userService': Requested bean is currently in creation: Is there an unresolvable circular reference?

Demo:复现循环依赖问题

我们来写一个最小可运行的 Spring Boot Demo。

Application.java:

@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}

UserService.javaOrderService.java 就是上面那两个。

运行后你就能看到启动失败,并且日志里清楚提示是循环依赖导致的。

重构代码,消除循环

第一种解决办法其实很直白:从业务逻辑上消除循环依赖

比如在上面的例子中,UserServiceOrderService 其实不应该互相依赖,而应该通过一个中间的 DAO 层 或者 Facade 层 来解耦。

优化后的写法:

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public void getUserInfo() {System.out.println("UserService: 获取用户信息");userRepository.findById(1L);}
}
@Service
public class OrderService {@Autowiredprivate OrderRepository orderRepository;public void getOrderInfo() {System.out.println("OrderService: 获取订单信息");orderRepository.findById(1L);}
}

这样 UserServiceOrderService 就互相独立了,循环依赖自然就消失了。

这也是最推荐的方式:设计层面尽量避免相互依赖

使用 @Lazy 注解

如果重构比较麻烦,Spring 还提供了一个小技巧:在其中一个依赖上加 @Lazy

@Service
public class UserService {@Autowired@Lazyprivate OrderService orderService;public void getUserInfo() {System.out.println("UserService: 获取用户信息");orderService.getOrderInfo();}
}

这样 Spring 在实例化 UserService 时不会立刻去创建 OrderService,而是等真正用到的时候才去注入。
这种方式适合于临时解决问题,但需要注意:

  • 如果两个 Bean 的依赖逻辑很复杂,@Lazy 可能只是在延迟报错;
  • 滥用 @Lazy 可能掩盖系统的设计问题。

避免构造函数循环注入

很多人为了写出不可变对象,会习惯性地用构造函数注入。但要注意,如果出现循环依赖,构造函数注入会直接挂掉,Spring 没法解决。

错误示例:

@Service
public class UserService {private final OrderService orderService;@Autowiredpublic UserService(OrderService orderService) {this.orderService = orderService;}
}
@Service
public class OrderService {private final UserService userService;@Autowiredpublic OrderService(UserService userService) {this.userService = userService;}
}

这种写法一旦循环依赖,Spring 根本没机会注入,就直接失败了。

因此在有可能产生循环依赖的场景下,建议用 Setter 注入字段注入,再配合 @Lazy,避免死循环。

实际场景中的思考

循环依赖问题并不只是技术 bug,更多时候反映了代码设计上的问题:

  • 如果两个 Service 互相调用,很可能是业务边界没有划清。
  • 如果一个 Service 同时依赖多个下游,可能说明它承担了过多职责,需要拆分。
  • 如果实在绕不开依赖,最好通过接口或者事件机制来解耦,而不是直接注入对方。

举个例子:
在电商系统中,订单服务库存服务 就是经典的相互依赖。订单需要库存确认才能生成,而库存也需要知道订单的结果来更新。
这类场景往往不是直接互相调用,而是通过 消息队列事件驱动 的方式解决。这样既避免了循环依赖,又提升了系统的解耦性。

总结

  • Spring Bean 循环依赖 本质是对象实例化和依赖注入的顺序问题。
  • 最好的解决办法是重构代码,消除循环
  • 如果实在改不了,可以用 @Lazy 延迟注入,或者改用 Setter 注入;
  • 构造函数注入要慎用,一旦循环依赖会直接报错。

从设计角度来看,循环依赖通常意味着模块职责划分不合理。与其依赖 Spring 的补救措施,不如在业务设计阶段就避免这种耦合。


文章转载自:

http://15RRsjvP.fwywr.cn
http://lWZpRW1M.fwywr.cn
http://mdmiizLh.fwywr.cn
http://c8MTPQ3e.fwywr.cn
http://d9NakJyw.fwywr.cn
http://zHPayzFV.fwywr.cn
http://OJUTeVYT.fwywr.cn
http://LmSVyYvn.fwywr.cn
http://x59etj9d.fwywr.cn
http://QcSHvthz.fwywr.cn
http://YCkEqWRl.fwywr.cn
http://h4NNC4UE.fwywr.cn
http://Sb9oHQP3.fwywr.cn
http://tMElrawd.fwywr.cn
http://0wkqXSFR.fwywr.cn
http://cFS8TWH1.fwywr.cn
http://PPGWEBnb.fwywr.cn
http://k7EM6c5e.fwywr.cn
http://rHHLRkob.fwywr.cn
http://enBmoa8x.fwywr.cn
http://FApQR1CA.fwywr.cn
http://OuWrcERR.fwywr.cn
http://wOaYFM8m.fwywr.cn
http://gyHPZXKB.fwywr.cn
http://MESV8ryC.fwywr.cn
http://H8JDKLQY.fwywr.cn
http://3Tzkk05y.fwywr.cn
http://v2zAMEzB.fwywr.cn
http://G1habBpR.fwywr.cn
http://JM6MMnCz.fwywr.cn
http://www.dtcms.com/a/379055.html

相关文章:

  • sdio NOT_AUTOGATING
  • 华为X考拉悠然 联合发布悠然智擎城市交通拥堵治理空间智能体方案
  • 《微服务事务管理》
  • CentOS 7 安装 MySQL 详细教程
  • 分布式锁解决集群下一人一单超卖问题
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘sweetviz’问题
  • @DateTimeFormat.fallbackPatterns 详解
  • 使用wavesurfer.js自定义波形绘制,集成频谱、时间轴、缩放、区域选择等插件
  • 数据库主从同步
  • leetcode27(两数之和)
  • Gradio全解11——Streaming:流式传输的视频应用(9)——使用FastRTC+Gemini创建沉浸式音频+视频的艺术评论家
  • 单片机 - I2C 总线
  • EasyExcel 实现国际化导入导出
  • 实现联邦学习客户端训练部分的示例
  • 从互联网医院系统源码到应用:智能医保购药平台的开发思路与实操经验
  • 伽马(gamma)变换记录
  • 第3节-使用表格数据-唯一约束
  • 深入浅出 C++20:新特性与实践
  • Java 面向对象三大核心思想:封装、继承与多态的深度解析
  • 蚁群算法详解:从蚂蚁觅食到优化利器
  • 星链计划 | 只赋能、不竞争!蓝卓“数智赋能·星链共生”重庆站沙龙成功举办
  • JavaScript 数组对象的属性、方法
  • vscode选择py解释器提示环境变量错误
  • 【2】标识符
  • Futuring robot旗下家庭机器人F1将于2025年面世
  • HTTPS 错误解析,常见 HTTPS 抓包失败、443 端口错误与 iOS 抓包调试全攻略
  • 利用数据分析提升管理决策水平
  • OC-KVC
  • Linux系统编程—基础IO
  • 考研408计算机网络2023-2024年第33题解析