如何解决 Spring Bean 循环依赖


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

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

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

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

展菲:您的前沿技术领航员

👋 大家好,我是展菲!

📱 全网搜索"展菲",即可纵览我在各大平台的知识足迹。

📣 公众号"Swift社区",每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。

💬 微信端添加好友"fzhanfei",与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。

📅 最新动态:2025 年 3 月 17 日

快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!

文章目录

前言

在用 Spring 开发项目时,经常会遇到一个让人头疼的问题------循环依赖

最典型的场景就是:A 注入了 BB 又注入了 A,结果 Spring 在启动的时候就直接报错了。

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

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

假设有两个业务类:

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

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

java 复制代码
@Service
public class UserService {

    @Autowired
    private OrderService orderService;

    public void getUserInfo() {
        System.out.println("UserService: 获取用户信息");
        orderService.getOrderInfo();
    }
}
java 复制代码
@Service
public class OrderService {

    @Autowired
    private UserService userService;

    public void getOrderInfo() {
        System.out.println("OrderService: 获取订单信息");
        userService.getUserInfo();
    }
}

启动项目的时候,Spring 容器在实例化 UserService 时需要先实例化 OrderService

OrderService 又需要 UserService,结果就会进入死循环,最终抛出 BeanCurrentlyInCreationException

报错信息一般长这样:

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

Demo:复现循环依赖问题

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

Application.java:

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

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

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

重构代码,消除循环

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

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

优化后的写法:

java 复制代码
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public void getUserInfo() {
        System.out.println("UserService: 获取用户信息");
        userRepository.findById(1L);
    }
}
java 复制代码
@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    public void getOrderInfo() {
        System.out.println("OrderService: 获取订单信息");
        orderRepository.findById(1L);
    }
}

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

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

使用 @Lazy 注解

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

java 复制代码
@Service
public class UserService {

    @Autowired
    @Lazy
    private OrderService orderService;

    public void getUserInfo() {
        System.out.println("UserService: 获取用户信息");
        orderService.getOrderInfo();
    }
}

这样 Spring 在实例化 UserService 时不会立刻去创建 OrderService,而是等真正用到的时候才去注入。

这种方式适合于临时解决问题,但需要注意:

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

避免构造函数循环注入

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

错误示例:

java 复制代码
@Service
public class UserService {

    private final OrderService orderService;

    @Autowired
    public UserService(OrderService orderService) {
        this.orderService = orderService;
    }
}
java 复制代码
@Service
public class OrderService {

    private final UserService userService;

    @Autowired
    public OrderService(UserService userService) {
        this.userService = userService;
    }
}

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

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

实际场景中的思考

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

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

举个例子:

在电商系统中,订单服务库存服务 就是经典的相互依赖。订单需要库存确认才能生成,而库存也需要知道订单的结果来更新。

这类场景往往不是直接互相调用,而是通过 消息队列事件驱动 的方式解决。这样既避免了循环依赖,又提升了系统的解耦性。

总结

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

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

相关推荐
我真的是大笨蛋2 小时前
从源码和设计模式深挖AQS(AbstractQueuedSynchronizer)
java·jvm·设计模式
爱吃烤鸡翅的酸菜鱼2 小时前
【Redis】常用数据结构之Hash篇:从常用命令到使用场景详解
数据结构·数据库·redis·后端·缓存·哈希算法
Pretend° Ω2 小时前
LRU缓存详解:用C语言实现高效数据管理
运维·c语言·spring·缓存·lru·双向链表
bobz9652 小时前
calico vxlan 模式如何实现和公有云一样的 VPC 功能?
后端
空山新雨(大队长)3 小时前
Java第五课:输入输出
java·开发语言
白云如幻3 小时前
【Java】QBC检索和本地SQL检索
java·数据库·sql
面汤放盐3 小时前
互联网“黑话”生存实用指南(100)
java·后端
@小匠4 小时前
iText与OpenPDF使用差异及中文处理完全指南
java·pdf
小鹭同学_4 小时前
JavaWeb05
java