SpringBoot中依赖循环问题的产生与解决方案

 更新时间:2025年12月01日 10:11:26   作者:保加利亚的风  
在使用SpringBoot开发项目时,我们经常会遇到一个令人头疼的问题——依赖循环,当两个或多个Bean相互依赖,形成闭环时,Spring容器就无法完成它们的初始化,本文将通过一个具体案例,深入剖析依赖循环是如何产生的,并提供多种实用的解决方案,需要的朋友可以参考下

引言

在使用 Spring Boot 开发项目时,我们经常会遇到一个令人头疼的问题——依赖循环(Circular Dependency)。当两个或多个 Bean 相互依赖,形成闭环时,Spring 容器就无法完成它们的初始化,从而抛出 BeanCurrentlyInCreationException 异常。

本文将通过一个具体案例,深入剖析依赖循环是如何产生的,并提供多种实用的解决方案,帮助你彻底掌握这一常见问题的应对之道。

一、什么是依赖循环?

依赖循环是指两个或多个 Spring Bean 在构造函数或字段注入时互相依赖,形成一个闭环。例如:

  • A 依赖 B
  • B 依赖 A
    这种情况下,Spring 容器在创建 A 时需要先创建 B,而创建 B 又需要先创建 A,陷入死循环。

二、依赖循环的典型场景(案例)

场景描述

假设我们正在开发一个用户服务系统,有两个核心组件:

  • UserService:负责用户相关业务逻辑
  • OrderService:负责订单处理,其中需要验证用户信息
    两者相互调用对方的方法,导致循环依赖。

代码示例

@Service
public class UserService {

    private final OrderService orderService;

    public UserService(OrderService orderService) {
        this.orderService = orderService;
    }

    public void handleUser() {
        System.out.println("Handling user...");
        orderService.checkOrders();
    }
}
@Service
public class OrderService {

    private final UserService userService;

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

    public void checkOrders() {
        System.out.println("Checking orders...");
        userService.handleUser(); // 实际中可能不是直接调用,但存在依赖关系
    }
}

启动报错

当你启动 Spring Boot 应用时,会看到类似如下错误:

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

这是因为 Spring 默认使用构造器注入(Constructor Injection),而构造器注入无法处理循环依赖(仅支持 setter 或 field 注入的三级缓存机制)。

三、为什么会出现依赖循环?

根本原因在于设计层面的耦合度过高。两个本应职责分离的服务,却互相持有对方的引用,违反了“单一职责原则”和“依赖倒置原则”。

虽然 Spring 框架在某些情况下(如 setter 注入)可以通过三级缓存机制解决循环依赖,但构造器注入 + 循环依赖 = 必然失败。

注意:Spring 仅支持单例 Bean 的 setter/field 注入下的循环依赖,不支持原型(Prototype)Bean 或构造器注入的循环依赖。

四、解决方案详解

方案一:重构代码,消除循环依赖(推荐)

这是最根本、最优雅的解决方式。

思路:

  • 提取公共逻辑到第三个服务
  • 使用事件驱动(ApplicationEvent)
  • 通过接口解耦

示例:引入 UserValidator 服务

@Service
public class UserValidator {
    public boolean isValid(Long userId) {
        // 验证逻辑
        return true;
    }
}

@Service
public class UserService {
    private final UserValidator userValidator;

    public UserService(UserValidator userValidator) {
        this.userValidator = userValidator;
    }

    public void handleUser() {
        System.out.println("Handling user...");
    }
}

@Service
public class OrderService {
    private final UserValidator userValidator;

    public OrderService(UserValidator userValidator) {
        this.userValidator = userValidator;
    }

    public void checkOrders() {
        if (userValidator.isValid(1L)) {
            System.out.println("Orders are valid.");
        }
    }
}

优点:职责清晰,无循环,易于测试和维护。

方案二:使用 @Lazy 延迟加载(快速修复)

在其中一个注入点上添加 @Lazy 注解,让 Spring 在实际使用时才初始化该 Bean。

@Service
public class UserService {

    private final OrderService orderService;

    public UserService(@Lazy OrderService orderService) {
        this.orderService = orderService;
    }

    // ...
}

Spring 会在创建 UserService 时注入一个代理对象,真正调用方法时才初始化 OrderService。

注意:这只是“绕过”问题,并未真正解决设计缺陷,仅适用于临时修复。

方案三:改用 Setter 或 Field 注入(不推荐)

将构造器注入改为 @Autowired 字段注入:

@Service
public class UserService {

    @Autowired
    private OrderService orderService;

    // ...
}

Spring 利用三级缓存(singletonObjects、earlySingletonObjects、singletonFactories)可以在这种情况下完成注入。

缺点:

  • 破坏了不可变性
  • 不利于单元测试
  • 违背 Spring 官方推荐的“优先使用构造器注入”原则

官方文档明确建议:尽可能使用构造器注入,因为它能保证依赖不为 null,且对象状态完整。

方案四:使用 ApplicationContext 手动获取 Bean(极端情况)

@Component
public class ServiceLocator implements ApplicationContextAware {

    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        ServiceLocator.context = applicationContext;
    }

    public static <T> T getBean(Class<T> clazz) {
        return context.getBean(clazz);
    }
}

// 在 UserService 中
public void someMethod() {
    OrderService orderService = ServiceLocator.getBean(OrderService.class);
    orderService.doSomething();
}

此方法破坏了 IoC 原则,应尽量避免。

五、总结

方案是否推荐说明
重构代码(提取公共逻辑)✅ 强烈推荐从根源解决问题,提升架构质量
使用 @Lazy⚠️ 谨慎使用快速修复,但掩盖设计问题
改用字段注入❌ 不推荐违背最佳实践,降低代码质量
手动获取 Bean❌ 不推荐破坏依赖注入原则

最佳实践建议:

  • 优先使用构造器注入
  • 避免服务之间直接相互依赖
  • 通过领域事件、消息队列或中介服务解耦
  • 定期审查 Bean 依赖图(可使用 spring-boot-starter-actuator 的 /beans 端点)

六、结语

依赖循环是 Spring 开发中的常见陷阱,但它也是一面镜子,反映出我们代码设计中的耦合问题。与其寻找“绕过”的技巧,不如借此机会优化架构,写出更清晰、更健壮的代码。

到此这篇关于SpringBoot中依赖循环问题的产生与解决方案的文章就介绍到这了,更多相关SpringBoot依赖循环问题内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringMvc+POI处理excel表数据导入

    SpringMvc+POI处理excel表数据导入

    这篇文章主要为大家详细介绍了SpringMvc+POI处理excel表数据导入,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • 实现分布式WebSocket集群的方法

    实现分布式WebSocket集群的方法

    本文总结出了几个实现分布式WebSocket集群的办法,从zuul到spring cloud gateway的不同尝试,总结出了这篇文章,希望能帮助到某些人,并且能一起分享这方面的想法与研究
    2022-03-03
  • Java进阶之SPI机制详解

    Java进阶之SPI机制详解

    Java SPI机制在很多大型中间建码,例如Dubbo中均有采用,属于高级Java开发的进阶必备知识点,务必要求掌握.文中有非常详细的代码示例及解释,需要的朋友可以参考下
    2021-05-05
  • 详解Java的Spring框架中bean的注入集合

    详解Java的Spring框架中bean的注入集合

    这篇文章主要介绍了详解Java的Spring框架中bean的注入集合,Spring是Java的SSH三大web开发框架之一,需要的朋友可以参考下
    2015-12-12
  • Spring中的集合注入代码实例

    Spring中的集合注入代码实例

    这篇文章主要介绍了Spring中的集合注入代码实例,集合注入是指在Spring框架中,通过配置文件或注解的方式将集合类型的数据注入到Bean中,集合类型包括List、Set、Map和Properties等,需要的朋友可以参考下
    2023-11-11
  • SpringBoot + Spring Cloud Consul 服务注册和发现详细解析

    SpringBoot + Spring Cloud Consul 服务注册和发现详细解析

    这篇文章主要介绍了SpringBoot + Spring Cloud Consul 服务注册和发现,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07
  • springboot查询全部部门流程分析

    springboot查询全部部门流程分析

    本文分析了在SpringBoot框架中前端如何请求DeptController的list()方法,并通过DeptService到DeptMapper接口查询数据库中的全部部门信息的流程,整个过程涉及前端到后端数据的获取和返回,是SpringBoot应用中常见的数据处理模式
    2024-10-10
  • Java实现彩色图片转换为灰度图片的示例代码

    Java实现彩色图片转换为灰度图片的示例代码

    将彩色图片转换为灰度图片是图像处理中的常见操作,通常用于简化图像、增强对比度、或者进行后续的图像分析,本项目的目标是通过Java实现将彩色图片转换为灰度图片,需要的朋友可以参考下
    2025-02-02
  • Java设计模式之代理模式与@Async异步注解失效的解决

    Java设计模式之代理模式与@Async异步注解失效的解决

    代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象
    2022-07-07
  • 详解Java高级特性之反射

    详解Java高级特性之反射

    这篇文章主要介绍了Java高级特性之反射的相关知识,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-06-06

最新评论