SpringBoot 循环依赖解决方案

循环依赖 :两个或多个 Bean 互相注入(A依赖B,B依赖A),Spring 默认只能解决单例 Bean 的 setter/字段注入循环依赖,构造器注入、多例、代理场景会直接报错。

我会分场景+解决方案,从最简单到最复杂讲清楚。


一、先判断:你的循环依赖属于哪种?

1. 最常见:单例 Bean + 字段/setter 注入(无报错)

Spring 自动解决,无需处理。

java 复制代码
@Service
public class A {
    @Autowired
    private B b; // 字段注入
}

@Service
public class B {
    @Autowired
    private A a;
}

正常运行,Spring 三级缓存天然支持。


2. 构造器注入循环依赖(必报错)

这是最常见的报错场景

java 复制代码
@Service
public class A {
    // 构造器注入 A 依赖 B
    public A(B b) {}
}

@Service
public class B {
    // 构造器注入 B 依赖 A
    public B(A a) {}
}

❌ 报错:Requested bean is currently in creation

解决方案(3种最优解)
方案1:改用字段/@Autowired 注入(最简单)

直接把构造器注入改成字段注入,Spring 自动解决。

方案2:使用 @Lazy 懒加载(推荐)

在构造器参数上加 @Lazy,创建临时代理对象,打破循环:

java 复制代码
@Service
public class A {
    public A(@Lazy B b) { // 关键:@Lazy
        this.b = b;
    }
}

@Service
public class B {
    public B(A a) {
        this.a = a;
    }
}
方案3:使用 ApplicationContext 手动获取 Bean
java 复制代码
@Service
public class A implements ApplicationContextAware {
    private B b;

    @Override
    public void setApplicationContext(ApplicationContext ctx) {
        this.b = ctx.getBean(B.class); // 手动获取
    }
}

3. 多例(prototype)Bean 循环依赖(必报错)

Spring 不解决多例循环依赖,无三级缓存支持。

解决方案
  1. 放弃多例:改成单例(90%场景适用)
  2. 手动创建对象:不用 Spring 管理依赖
  3. 使用 Provider 延迟注入
java 复制代码
@Component
@Scope("prototype")
public class A {
    @Autowired
    private ObjectProvider<B> bProvider;
    
    public B getB() {
        return bProvider.getObject();
    }
}

4. 代理类导致的循环依赖(AOP/事务)

开启 AOP/@Transactional 后,Bean 被代理,默认三级缓存会失效,报错。

解决方案
方案1:启动类加配置(最省事)
java 复制代码
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        // 允许循环依赖
        System.setProperty("spring.main.allow-circular-references", "true");
        SpringApplication.run(App.class, args);
    }
}
方案2:yml 配置
yaml 复制代码
spring:
  main:
    allow-circular-references: true

5. 终极方案:重构代码(最规范,推荐)

循环依赖本质是代码设计不合理,最好从根源解决:

  1. 抽取公共类:把 A、B 共用逻辑抽到 C,A、B 都依赖 C
  2. 使用事件/观察者模式:解耦互相依赖
  3. 使用方法调用:不用注入 Bean,直接调用方法

示例(抽取公共类):

java 复制代码
// 公共逻辑
@Service
public class C {}

@Service
public class A {
    @Autowired private C c;
}

@Service
public class B {
    @Autowired private C c;
}

✅ 彻底消除循环依赖,代码更优雅。


二、快速排查工具

如果你不知道哪里循环依赖,开启日志:

yaml 复制代码
logging:
  level:
    org.springframework: DEBUG

日志会打印完整依赖链,直接定位报错 Bean。


三、总结:按场景选择方案

依赖场景 是否报错 解决方案
单例+字段注入 无需处理
构造器注入 @Lazy / 字段注入
多例Bean 改单例 / ObjectProvider
AOP/事务代理 开启 allow-circular-references
所有场景 - 重构代码(最优)

核心记住

  1. Spring 只自动解决单例+字段/setter 注入的循环依赖
  2. 构造器注入用 @Lazy 最快解决
  3. 生产环境优先重构代码,不要依赖配置强行开启循环依赖
相关推荐
chenzhou__5 小时前
独立游戏开发日志 ①:从信号博弈到涂色对战——一次玩法重构始
笔记·后端·学习·unity·go·独立游戏
CodeSheep5 小时前
hvv员工家属爆料:老公22年入职的,普通员工,老是被误以为高薪多奖金,实际上还没配股,暂时钱没存多少,我倒是很心疼他工作辛苦。
前端·后端·程序员
IT_陈寒5 小时前
Redis这个内存杀手坑惨了我,排查三小时发现是过期策略搞的鬼
前端·人工智能·后端
花生了什么事o5 小时前
Docker 部署 SpringBoot:从镜像构建到服务器运行
服务器·spring boot·docker
摇滚侠5 小时前
《SpringBoot 3:入门与应用实战》第 15 章 生产级特性 使用监控体系 阅读笔记
spring boot·笔记·后端
倔强的石头1065 小时前
Spring Boot 接入金仓数据库:配置分层、启动自检与常见错误
数据库·spring boot·后端
五仁烧饼5 小时前
Unity Addressables 资源依赖治理:规则约束、自动修复与全链路防护
java·unity·游戏引擎
spider_xcxc5 小时前
Jenkins 遇见 Argo CD:构建可靠 GitOps 流水线的实战指南
java·开发语言·servlet
AI人工智能+电脑小能手5 小时前
【大白话说Java面试题 第214题】【10_网络协议篇】第5题:说一下 TCP 协议的三次握手和四次挥手
java·网络协议·tcp·三次握手·四次挥手
人间凡尔赛5 小时前
2026 云原生后端架构演进:事件驱动、虚拟线程与 AI Agent 内嵌,三驾马车如何重塑技术栈
后端·云原生·架构