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

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括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 来获取用户信息。

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

@Service
public class UserService {

    @Autowired
    private OrderService orderService;

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

    @Autowired
    private 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 {

    @Autowired
    private UserRepository userRepository;

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

    @Autowired
    private OrderRepository orderRepository;

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

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

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

使用 @Lazy 注解

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

@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 没法解决。

错误示例:

@Service
public class UserService {

    private final OrderService orderService;

    @Autowired
    public UserService(OrderService orderService) {
        this.orderService = orderService;
    }
}
@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 的补救措施,不如在业务设计阶段就避免这种耦合。

Logo

加入「COC·上海城市开发者社区」,成就更好的自己!

更多推荐