当前位置: 首页 > news >正文

Spring 循环依赖解析与解决方案

文章目录

    • 1. 什么是循环依赖?
      • 1.1 概念解析
      • 1.2 示例代码
    • 2. 循环依赖的类型
      • 2.1 构造器循环依赖(不可解决 ❌)
      • 2.2 Setter 方式或 `@Autowired` 方式的循环依赖(可解决 ✅)
    • 3. 解决循环依赖的方式
      • 3.1 方式一:使用 `@Lazy`(延迟加载) 🚀
      • 3.2 方式二:修改依赖关系 🛠️
      • 3.3 方式三:使用 `@PostConstruct` 进行初始化 🎯
      • 3.4 方式四:使用 `ApplicationContextAware` 直接获取 Bean ⚙️
    • 4. 结论与最佳实践 🏆

1. 什么是循环依赖?

1.1 概念解析

在 Spring 容器中,循环依赖(Circular Dependency) 是指:

  • Bean A 依赖 Bean B
  • Bean B 依赖 Bean A
  • 这种情况下,Spring 在创建 Bean 时会出现互相等待,可能导致异常。

1.2 示例代码

@Component
public class A {
    private final B b;

    @Autowired
    public A(B b) {
        this.b = b;
    }
}

@Component
public class B {
    private final A a;

    @Autowired
    public B(A a) {
        this.a = a;
    }
}

💥 错误信息:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'A': Requested bean is currently in creation:
Is there an unresolvable circular reference?

2. 循环依赖的类型

2.1 构造器循环依赖(不可解决 ❌)

  • 通过构造方法互相依赖,Spring 无法提前暴露 Bean 实例,因此会直接报错。
  • 示例:(上面代码就属于此类)

2.2 Setter 方式或 @Autowired 方式的循环依赖(可解决 ✅)

  • Spring 默认支持这种循环依赖(单例模式下)。
  • 示例:
@Component
public class A {
    private B b;
    
    @Autowired
    public void setB(B b) {
        this.b = b;
    }
}

@Component
public class B {
    private A a;
    
    @Autowired
    public void setA(A a) {
        this.a = a;
    }
}

📌 Spring 解决方式:

  • 一级缓存(singletonObjects): 存放完全实例化的 Bean。
  • 二级缓存(earlySingletonObjects): 存放提前暴露的半成品 Bean。
  • 三级缓存(singletonFactories): 存放创建 Bean 的工厂,以支持代理(如 AOP 代理)。

3. 解决循环依赖的方式

3.1 方式一:使用 @Lazy(延迟加载) 🚀

  • 只在需要时才创建 Bean,避免初始化时发生循环依赖。
@Component
public class A {
    private final B b;
    
    @Autowired
    public A(@Lazy B b) {
        this.b = b;
    }
}

适用于构造器注入,但不能解决所有情况。

3.2 方式二:修改依赖关系 🛠️

  • 重新设计 Bean 关系,避免直接互相依赖。
  • 可以通过 引入中间层(如事件监听模式、消息队列等) 解决。

3.3 方式三:使用 @PostConstruct 进行初始化 🎯

  • 让 Spring 先创建 Bean,然后在初始化时再注入依赖。
@Component
public class A {
    private B b;

    @Autowired
    public A() {}

    @PostConstruct
    public void init() {
        this.b = SpringContextUtil.getBean(B.class);
    }
}

避免构造方法循环依赖,适用于某些场景。

3.4 方式四:使用 ApplicationContextAware 直接获取 Bean ⚙️

@Component
public class A implements ApplicationContextAware {
    private B b;
    private ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    
    @PostConstruct
    public void init() {
        this.b = applicationContext.getBean(B.class);
    }
}

适用于特定场景,但不推荐滥用 ApplicationContext,影响 Spring 设计模式。

4. 结论与最佳实践 🏆

方案适用情况备注
@Lazy适用于构造器循环依赖延迟加载可能会影响性能
修改依赖结构推荐最优方案,从根本上解决问题
@PostConstruct适用于部分场景但可能导致依赖获取不直观
ApplicationContext 获取 Bean可行但不推荐破坏 Spring 设计模式

🎯 最佳实践:

  1. 避免构造器循环依赖,优先使用 @Autowired 方式。
  2. 重构业务逻辑,减少不必要的循环依赖。
  3. 若无法避免,尝试 @Lazy@PostConstruct 方案。
  4. **尽量不使用 ApplicationContextAware,防止耦合过高。 **

相关文章:

  • springcloud nacos 整合seata解决分布式事务
  • Android14窗口管理自适应投屏分辨率
  • 如何在视频中提取关键帧?
  • MySQL undo log,redo log和bin log日志文件的生成时间点、层级归属、存储位置及生命周期详解
  • Apache DolphinScheduler系列1-单节点部署及测试报告
  • ASP.NET Core 8.0学习笔记(二十八)——EFCore反向工程
  • React + TypeScript 复杂布局开发实战
  • 存储引擎、索引、SQL优化(MySQL笔记第四期)
  • Dashboard-frps
  • 【Azure 架构师学习笔记】- Azure Databricks (12) -- Medallion Architecture简介
  • 算法-数据结构(图)-DFS深度优先遍历
  • Cesium@1.126.0,创建3D瓦片,修改样式
  • 微信小程序网络请求与API调用:实现数据交互
  • Part-DB部署
  • 探索浮点数在内存中的存储(附带快速计算补码转十进制)
  • 网易云音乐分布式KV存储实践与演进
  • 博客系统完整开发流程
  • 观成科技:海莲花“PerfSpyRAT”木马加密通信分析
  • RK3399 Android10双WiFi功能实现
  • ubuntu+aarch64+dbeaver安装【亲测,避坑】
  • 联合国:欢迎俄乌伊斯坦布尔会谈,希望实现全面停火
  • 租车订单时隔7年从花呗免密扣费?“GoFun出行”引质疑
  • 澎湃·镜相第二届非虚构写作大赛初选入围名单公示
  • 普京批准俄方与乌克兰谈判代表团人员名单
  • “女硕士失踪13年生两孩”案进入审查起诉阶段,哥哥:妹妹精神状态好转
  • 孙卫东会见巴基斯坦驻华大使:支持巴印两国实现全面持久停火