Spring循环依赖​​的解决方式详解

 更新时间:2025年05月10日 11:48:48   作者:二进制11  
这篇文章主要介绍了Spring循环依赖​​的解决方式,​​循环依赖​​是指两个或多个Bean互相依赖,形成闭环,导致Spring无法正常完成依赖注入,需要的朋友可以参考下

什么是循环依赖

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

@Component
public class A {
    @Autowired
    private B b;
}
@Component
public class B {
    @Autowired
    private A a;
}

在这个例子中,A依赖B,B又依赖A,形成了一个循环依赖链。

Spring解决循环依赖的机制

Spring通过三级缓存机制来解决循环依赖问题,具体实现如下:

三级缓存结构

  • 一级缓存(singletonObjects):存储完全初始化好的Bean
  • 二级缓存(earlySingletonObjects):存储提前暴露的原始Bean(尚未填充属性)
  • 三级缓存(singletonFactories):存储Bean工厂,用于生成原始Bean的早期引用

解决流程

Spring解决循环依赖的核心流程如下:

详细步骤解析

创建Bean A:

  • Spring容器开始创建Bean A
  • 实例化A(调用构造方法)
  • 将A的ObjectFactory放入三级缓存(singletonFactories)
  • 准备填充A的属性

发现依赖B:

  • 在填充A的属性时,发现需要注入Bean B
  • 容器开始创建Bean B

创建Bean B:

  • 实例化B(调用构造方法)
  • 将B的ObjectFactory放入三级缓存(singletonFactories)
  • 准备填充B的属性

发现依赖A:

  • 在填充B的属性时,发现需要注入Bean A
  • 容器尝试从一级缓存获取A(未找到)
  • 从二级缓存获取A(未找到)
  • 从三级缓存获取A的ObjectFactory并调用getObject()获取早期引用
  • 将A的早期引用放入二级缓存,并从三级缓存移除

完成B的创建:

  • 将B的属性填充完整
  • 执行B的初始化后处理器
  • 将完整的B放入一级缓存
  • 从二级和三级缓存中移除B的相关条目

完成A的创建:

  • 现在A可以获取到完整的B实例
  • 将A的属性填充完整
  • 执行A的初始化后处理器
  • 将完整的A放入一级缓存
  • 从二级缓存中移除A

代码层面的实现

Spring解决循环依赖的核心代码在DefaultSingletonBeanRegistry类中:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 首先检查一级缓存
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            // 检查二级缓存
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 检查三级缓存
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    // 从三级缓存移到二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

循环依赖的限制

Spring并非能解决所有类型的循环依赖,有以下限制:

构造器注入:无法解决构造器注入的循环依赖

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

这种情况会抛出BeanCurrentlyInCreationException

原型(prototype)作用域的Bean:Spring不支持原型Bean的循环依赖

@Async方法:如果循环依赖中包含@Async方法,也可能出现问题

最佳实践

  • 尽量避免循环依赖,设计时应考虑解耦
  • 如果必须使用循环依赖,优先使用setter注入而非构造器注入
  • 考虑使用@Lazy注解延迟加载其中一个Bean
@Component
public class A {
    @Lazy
    @Autowired
    private B b;
}

总结

Spring通过三级缓存机制巧妙地解决了setter/field注入方式的循环依赖问题。理解这一机制不仅有助于我们避免开发中的循环依赖陷阱,也能更深入地理解Spring容器的Bean生命周期管理。在实际开发中,我们应当合理设计Bean之间的依赖关系,尽量避免循环依赖,当确实需要时,也要了解其背后的原理和限制。

以上就是Spring循环依赖​​的解决方式详解的详细内容,更多关于Spring循环依赖的资料请关注脚本之家其它相关文章!

相关文章

  • JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

    JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

    本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如indexOf()方法,并通过一个算法题目来应用这些知识,感兴趣的朋友一起看看吧
    2025-01-01
  • Spring源码解密之默认标签的解析

    Spring源码解密之默认标签的解析

    这篇文章主要给大家介绍了关于Spring源码解密之默认标签的解析的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-01-01
  • java中struts2实现文件上传下载功能实例解析

    java中struts2实现文件上传下载功能实例解析

    这篇文章主要介绍了java中struts2实现文件上传下载功能的方法,以实例形式较为详细的分析了struts2实现文件上传下载功能的具体实现技巧与相关问题的解决方法,具有一定的参考借鉴价值,需要的朋友可以参考下
    2015-01-01
  • Java 中HashCode作用_动力节点Java学院整理

    Java 中HashCode作用_动力节点Java学院整理

    这篇文章主要介绍了Java 中HashCode作用以及hashcode对于一个对象的重要性,对java中hashcode的作用相关知识感兴趣的朋友一起学习吧
    2017-05-05
  • JVM的垃圾回收算法一起来看看

    JVM的垃圾回收算法一起来看看

    这篇文章主要为大家详细介绍了JVM的垃圾回收算法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • springBoot集成flowable的流程解析

    springBoot集成flowable的流程解析

    这篇文章主要介绍了springBoot集成flowable的流程,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-02-02
  • JavaWeb开发基于ssm的校园服务系统(实例详解)

    JavaWeb开发基于ssm的校园服务系统(实例详解)

    这篇文章主要介绍了JavaWeb开发基于ssm的校园服务系统,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-02-02
  • netty服务端辅助类ServerBootstrap创建逻辑分析

    netty服务端辅助类ServerBootstrap创建逻辑分析

    这篇文章主要介绍了netty服务端辅助类ServerBootstrap创建逻辑分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-03-03
  • springboot如何添加全局异常捕获类

    springboot如何添加全局异常捕获类

    这篇文章主要介绍了springboot如何添加全局异常捕获类,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • Spring外部化配置的几种技巧分享

    Spring外部化配置的几种技巧分享

    在油管上看了龙之春的一个Spring tips 视频,讲述Spring外部化配置的几种技巧,收获颇多,想拿出来给大家分享下。对spring感兴趣的朋友可以了解下本文
    2021-06-06

最新评论