深度解析SpringBoot循环依赖问题与解决

 更新时间:2025年06月11日 08:04:42   作者:码农阿豪@新空间  
在Spring Boot开发过程中,循环依赖(Circular Dependency)是一个常见但棘手的问题,本文将通过两个典型案例,深入分析循环依赖问题的根源,并提供多种解决方案,希望对大家有所帮助

引言

在Spring Boot开发过程中,循环依赖(Circular Dependency)是一个常见但棘手的问题。当两个或多个Bean相互依赖时,Spring容器无法确定初始化顺序,导致应用启动失败。本文将通过两个典型案例,深入分析循环依赖问题的根源,并提供多种解决方案,帮助开发者彻底理解和解决这类问题。

1. 什么是循环依赖

循环依赖是指两个或多个Bean相互引用,形成闭环依赖关系。例如:

  • BeanA 依赖 BeanB
  • BeanB 依赖 BeanC
  • BeanC 又依赖 BeanA

Spring默认禁止循环依赖,因为它可能导致不可预测的行为,如NPE(NullPointerException)或初始化顺序问题。

2. 案例一:Shiro与Service层的循环依赖

问题分析

错误日志如下:

The dependencies of some of the beans in the application context form a cycle:
   shirFilter → securityManager → userRealm → sysUserService → sysRoleService → sysUserService

这是一个典型的服务层与Shiro安全框架的循环依赖问题:

  • ShiroFilter 依赖 SecurityManager
  • SecurityManager 依赖 UserRealm
  • UserRealm 依赖 SysUserService
  • SysUserService 依赖 SysRoleService
  • SysRoleService 又依赖 SysUserService,形成闭环。

解决方案

(1) 重构代码,消除循环依赖(推荐)

// 将 SysUserService 和 SysRoleService 的相互依赖改为单向依赖
@Service
public class SysUserServiceImpl implements SysUserService {
    // 不再直接依赖 SysRoleService
    // 改为通过方法参数传入
    public void someMethod(SysRoleService roleService) {
        // ...
    }
}

(2) 使用 @Lazy 延迟加载

@Service
public class SysUserServiceImpl implements SysUserService {
    @Lazy  // 延迟注入,避免循环依赖
    @Autowired
    private SysRoleService sysRoleService;
}

(3) 使用 Setter 注入替代字段注入

@Service
public class SysUserServiceImpl implements SysUserService {
    private SysRoleService sysRoleService;

    @Autowired  // 使用Setter注入,Spring会在Bean初始化后再注入依赖
    public void setSysRoleService(SysRoleService sysRoleService) {
        this.sysRoleService = sysRoleService;
    }
}

(4) 临时启用循环引用(不推荐)

# application.properties
spring.main.allow-circular-references=true

3. 案例二:PageHelper自动配置循环依赖

问题分析

错误日志:

The dependencies of some of the beans in the application context form a cycle:
   com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration

这个问题通常是由于 PageHelper 版本与 Spring Boot 不兼容,或者自动配置类自身存在循环引用。

解决方案

(1) 升级 PageHelper 版本

<!-- pom.xml -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.6</version> <!-- 推荐使用最新稳定版 -->
</dependency>

(2) 排除自动配置

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

(3) 手动配置 PageHelper

@Configuration
public class PageHelperConfig {
    @Bean
    public PageInterceptor pageInterceptor() {
        PageInterceptor interceptor = new PageInterceptor();
        Properties props = new Properties();
        props.setProperty("helperDialect", "mysql");
        props.setProperty("reasonable", "true");
        interceptor.setProperties(props);
        return interceptor;
    }
}

(4) 检查依赖冲突

mvn dependency:tree

确保没有引入多个不同版本的PageHelper。

4. 循环依赖的通用解决策略

方案适用场景优点缺点
重构代码长期项目彻底解决问题可能需要较大改动
@Lazy简单循环依赖改动小可能隐藏设计问题
Setter注入需要控制初始化顺序符合Spring推荐方式代码稍显冗长
允许循环引用紧急修复快速解决不推荐长期使用

5. 最佳实践与总结

最佳实践

避免双向依赖:尽量采用单向依赖,如 A → B,而不是 A ↔ B。

使用接口分离:将公共逻辑提取到单独接口,减少耦合。

优先使用构造器注入:

@Service
public class MyService {
    private final OtherService otherService;
    
    @Autowired
    public MyService(OtherService otherService) {
        this.otherService = otherService;
    }
}

定期检查依赖冲突:使用 mvn dependency:tree 或 gradle dependencies。

总结

循环依赖问题虽然常见,但通过合理的架构设计、依赖管理和Spring提供的机制(如@Lazy、Setter注入),可以有效解决。长期来看,重构代码、优化设计是最佳方案,而临时方案(如allow-circular-references)仅适用于紧急修复。

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

相关文章

  • 基于Java检查IPv6地址的合法性

    基于Java检查IPv6地址的合法性

    这篇文章主要介绍了基于Java检查IPv6地址的合法性,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • 详解Java单例模式的实现与原理剖析

    详解Java单例模式的实现与原理剖析

    单例模式是Java中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。本文将详解单例模式的实现及原理剖析,需要的可以参考一下
    2022-05-05
  • java中vector与hashtable操作实例分享

    java中vector与hashtable操作实例分享

    java中vector与hashtable操作实例,有需要的朋友可以参考一下
    2014-01-01
  • Java 10的10个新特性总结

    Java 10的10个新特性总结

    这篇文章我们给大家整理了关于Java 10的10个新特性以及相关要点内容,有兴趣的朋友们可以参考下。
    2018-08-08
  • Java资源加载机制及使用最佳实践

    Java资源加载机制及使用最佳实践

    Java类加载器的资源加载机制,包括`getResource`、`getResources`、`findResource`、`findResources`等方法,本文给大家介绍Java资源加载机制及使用最佳实践,感兴趣的朋友跟随小编一起看看吧
    2025-12-12
  • 如何使用Idea中的 Deployment 实现打包自动部署

    如何使用Idea中的 Deployment 实现打包自动部署

    这篇文章主要介绍了使用Idea中的 Deployment 实现打包自动部署,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • 使用springBoot中的info等级通过druid打印sql

    使用springBoot中的info等级通过druid打印sql

    这篇文章主要介绍了使用springBoot中的info等级通过druid打印sql,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java 中很好用的数据结构EnumSet

    Java 中很好用的数据结构EnumSet

    这篇文章主要介绍了Java 中很好用的数据结构EnumSet,EnumMap即属于一个Map,下文围绕主题展开详细内容,需要的小伙伴可以参考参考一下
    2022-05-05
  • 使用easyexcel导出的excel文件,使用poi读取时异常处理方案

    使用easyexcel导出的excel文件,使用poi读取时异常处理方案

    这篇文章主要介绍了使用easyexcel导出的excel文件,使用poi读取时异常处理方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • 使用Spring MVC实现双向数据绑定

    使用Spring MVC实现双向数据绑定

    Spring MVC是一个广泛用于构建Java Web应用程序的框架,它提供了众多功能,包括双向数据绑定,在这篇文章中,我们将向Java新手介绍如何使用Spring MVC实现双向数据绑定,以及为什么这个特性如此重要,需要的朋友可以参考下
    2024-01-01

最新评论