Springboot循环依赖

什么是循环依赖

循环依赖(Circular Dependency) 是指两个或多个 Bean 相互直接或间接依赖,导致容器无法正常初始化这些 Bean。

java 复制代码
@Service
public class ServiceA {
    @Autowired
    private ServiceB serviceB; // ServiceA 依赖 ServiceB
}

@Service
public class ServiceB {
    @Autowired
    private ServiceA serviceA; // ServiceB 依赖 ServiceA
}

Spring Boot 基于 Spring 框架,其循环依赖的处理机制与 Spring 一致,但在 Spring Boot 2.6+ 版本中默认禁止了循环依赖 (通过 spring.main.allow-circular-references=false)。

产生循环依赖的原因

1.构造函数注入循环依赖

java 复制代码
@Service
public class ServiceA {
    private final ServiceB serviceB;
    public ServiceA(ServiceB serviceB) { // 构造函数注入
        this.serviceB = serviceB;
    }
}

@Service
public class ServiceB {
    private final ServiceA serviceA;
    public ServiceB(ServiceA serviceA) { // 构造函数注入
        this.serviceA = serviceA;
    }
}
  • 直接报错 :构造函数注入的循环依赖无法解决,容器启动时抛出 BeanCurrentlyInCreationException

2.Setter/Field 注入循环依赖

在spring中使用@Autowired注解标签进行自动注入,如果不加以处理,会出现循环依赖问题 。

怎么解决循环依赖

在Springboot2.5以前可以通过三级缓存解决单例 Bean 的循环依赖问题。

缓存名称 职责
singletonObjects 存放完全初始化好的 Bean(一级缓存)
earlySingletonObjects 存放提前暴露的早期 Bean(二级缓存)
singletonFactories 存放 Bean 的工厂对象(三级缓存)

以最初的ServiceA与ServiceB为例,

  1. 创建 ServiceA,通过工厂将其半成品引用存入三级缓存。

  2. ServiceA 注入 ServiceB,触发 ServiceB 的创建。

  3. 创建 ServiceB,同样将其半成品引用存入三级缓存。

  4. ServiceB 注入 ServiceA 时,从三级缓存中获取 ServiceA 的早期引用,完成 ServiceB 的初始化。

  5. ServiceB 初始化完成后,ServiceA 完成依赖注入,最终初始化。

出现循环依赖之后的几个解决思路:

1.避免循环依赖(推荐)

  • 重构代码:将公共逻辑抽离到第三个 Bean 中。

  • 使用接口或抽象类:通过面向接口编程解耦具体实现。

2. 允许循环依赖(临时方案)

application.properties 中显式允许循环依赖:

java 复制代码
# Spring Boot 2.6+ 需要手动开启
spring.main.allow-circular-references=true

这种只适用于Springboot版本在2.6以上的循环依赖被禁止的情形。

3. 使用 @Lazy 延迟加载

在其中一个依赖上添加 @Lazy,延迟注入 Bean 的初始化:

java 复制代码
@Service
public class ServiceA {
    @Lazy
    @Autowired
    private ServiceB serviceB; // 延迟初始化 ServiceB
}

4. 调整注入方式

优先使用 Setter/Field 注入:避免构造函数注入导致的不可解循环依赖。

java 复制代码
@Service
public class ServiceA {
    private ServiceB serviceB;
    
    @Autowired
    public void setServiceB(ServiceB serviceB) { // Setter 注入
        this.serviceB = serviceB;
    }
}

使用setter注入

循环依赖的局限性

  • 构造函数注入无法解决循环依赖:Spring 容器在创建 Bean 时需先完成构造函数调用,此时依赖的 Bean 尚未初始化。

  • 原型(Prototype)作用域的 Bean:Spring 不管理原型 Bean 的完整生命周期,无法解决其循环依赖。

  • AOP 代理问题 :如果 Bean 被 AOP 代理(如 @Async@Transactional),可能导致循环依赖解决失败。

总结

Spring Boot 的循环依赖本质是 Spring 框架的机制问题,解决核心在于:

  1. 理解三级缓存的工作原理。

  2. 优先通过代码设计避免循环依赖。

  3. 必要时合理使用 @Lazy 或调整注入方式。

尽可能在设计之初就避免循环依赖

相关推荐
韩立学长35 分钟前
基于Springboot泉州旅游攻略平台d5h5zz02(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·旅游
摇滚侠1 小时前
在 SpringBoot 项目中,开发工具使用 IDEA,.idea 目录下的文件需要提交吗
java·spring boot·intellij-idea
打工的小王2 小时前
Spring Boot(三)Spring Boot整合SpringMVC
java·spring boot·后端
毕设源码-赖学姐2 小时前
【开题答辩全过程】以 高校体育场馆管理系统为例,包含答辩的问题和答案
java·spring boot
vx_Biye_Design3 小时前
【关注可免费领取源码】房屋出租系统的设计与实现--毕设附源码40805
java·spring boot·spring·spring cloud·servlet·eclipse·课程设计
翱翔-蓝天3 小时前
为什么“看起来很规范”的后端项目反而臃肿且性能下降
spring boot
80530单词突击赢4 小时前
JavaWeb进阶:SpringBoot核心与Bean管理
java·spring boot·后端
long3165 小时前
Aho-Corasick 模式搜索算法
java·数据结构·spring boot·后端·算法·排序算法
独断万古他化5 小时前
【SSM开发实战:博客系统】(三)核心业务功能开发与安全加密实现
spring boot·spring·mybatis·博客系统·加密
rannn_1115 小时前
【苍穹外卖|Day4】套餐页面开发(新增套餐、分页查询、删除套餐、修改套餐、起售停售)
java·spring boot·后端·学习