Spring源码剖析之Spring处理循环依赖的问题

下面就是关于“Spring源码剖析之Spring处理循环依赖的问题”的完整攻略。

标题:Spring源码剖析之Spring处理循环依赖的问题

什么是循环依赖?

循环依赖指的是在Spring容器初始化bean时,A对象依赖B对象,同时B对象又依赖A对象。这种情况下,Spring无法推断依赖关系,会抛出BeanCurrentlyInCreationException异常。

Spring提供了三种处理循环依赖的方法:

  1. 构造函数方式的循环依赖;
  2. Set方式的循环依赖;
  3. 通过AOP CGLIB代理方式的循环依赖。

构造函数方式的循环依赖

构造函数方式的循环依赖是指,两个或多个bean在实例化的时候就互相依赖。当Spring创建Bean时,发现A依赖B,于是会先去创建B对象;而B依赖A,于是也会去创建A对象。这就会导致两个对象互相等待初始化。

针对构造函数方式的循环依赖,Spring提供了一个“提前暴露Bean”(Eagerly-Exposed Bean,EJB)的机制。在将JavaBean实例化的时候,会分为实例化和初始化两步,在初始化过程中注入属性信息。这时候如果A依赖B,而B又依赖A,那么再A实例化完并且成功初始化时,B依赖A的属性已经赋值成功,这时候B就可以正常地初始化注入完成。因此,可以通过将需要提前初始化的 Bean、早些暴露出来,从而解决构造函数方式的循环依赖问题。

Set方式的循环依赖

针对Set方式的循环依赖,Spring采用了一个“两步注入”(two-stage injection)的机制。当Bean初始化完成后,容器会将Bean实例化后的对象放入到“实例化完成”的Map容器中,然后开始Bean初始化的工作。在初始化完成前,Spring还会特殊处理一下当前Bean所需要的其他Bean。当注入Bean B时发现 B 还需要注入 A,而 A 目前还未完成注入,此时Spring先自动将B注入一个代理对象,然后将B放入到“正在初始化的Bean”Map容器中,等到 B 初始化完成后,再让 B 代理对象把 A 赋值给 B,从而解决了循环依赖的问题。

AOP CGLIB代理方式的循环依赖

当依赖关系出现循环调用时,我们还可以采用 AOP+CGLIB 的方式控制依赖关系的实例化和属性注入。Spring利用AOP的技术,对需要循环回调的Bean通过CGLIB动态代理构建一个代理对象叫做“本地代理”,然后使用本地代理对象解析AOP方法时注入所依赖的Bean对象。

示例说明

下面是两个示例说明,分别说明了构造函数方式和Set方式的循环依赖。

示例1:构造函数方式的循环依赖

public class A {
    private B b;

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

public class B {
    private A a;

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

@Configuration
public class AppConfig {
    @Bean
    public A a() {
        return new A(b());
    }

    @Bean
    public B b() {
        return new B(a());
    }
}

当我们在执行AppConfig@Bean时,就会抛出循环依赖的异常。

如何解决呢?我们可以使用@Configuration的属性@DependsOn来解决。

@Configuration
public class AppConfig {
    @Bean
    public A a() {
        return new A(b());
    }

    @Bean
    @DependsOn("a")
    public B b() {
        return new B(a());
    }
}

在这个例子中,我们告诉Spring在初始化Bean b的时候,先初始依赖的Bean a,这样就不会再产生循环依赖的问题。

示例2:Set方式的循环依赖

public class A {
   private B b;
   public A() {
   }

   public void setB(B b) {
     this.b = b;
   }
}

public class B {
   private A a;

   public B() {
   }

   public void setA(A a) {
     this.a = a;
   }
}

如果我们将上面的A和B两个类放入Spring容器中,即:

<bean id="a" class="A" />
<bean id="b" class="B" />

则会抛出异常提示:

Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

解决方式:

我们可以使用@Autowired@Resource来注入依赖,如下。

public class A {
    @Resource
    private B b;
}

public class B {
    @Autowired
    private A a;
}

这样,Spring会在执行到A或B的时候,先实例化自己再注入依赖的Bean,从而避免了循环依赖的问题。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring源码剖析之Spring处理循环依赖的问题 - Python技术站

(0)
上一篇 2023年5月31日
下一篇 2023年5月31日

相关文章

  • Java实现分布式系统限流

    Java实现分布式系统限流攻略 本文主要介绍如何在Java分布式系统中实现限流功能。限流是一种保护系统稳定性的重要手段,可以有效地避免系统被过量流量攻击、系统资源被耗尽等问题。 什么是限流? 限流是一种系统资源保护机制,通过对系统请求流量进行控制,保证系统能够承受的负载范围内运行。限流可以在短时间内有效地防止系统被过量流量冲垮,保障系统的可用性和稳定性。 常…

    Java 2023年5月30日
    00
  • 谈谈Java中的守护线程与普通线程

    Java中的线程分为两种类型——守护线程(Daemon Thread)和普通线程(User Thread)。守护线程是一种特殊的线程,它在后台运行,主要用于Java虚拟机的一些特定操作,比如垃圾回收和内存管理等。普通线程指的是用户线程,它是我们常规开发使用的线程。 定义 在Java中通过Thread类的构造函数和setDaemon方法设置线程的daemon属…

    Java 2023年5月19日
    00
  • jsp传值本地无乱码测试机出现乱码问题解决

    下面我将详细讲解“jsp传值本地无乱码测试机出现乱码问题解决”的完整攻略。 一、问题描述 当我们在本地运行jsp页面传输值,并将页面上传至测试机运行时,有时会出现中文乱码的问题。这是因为本地和测试机的编码不一致所造成的。 二、解决方法 配置jsp页面编码方式 在jsp页面中,加入以下代码进行页面编码的设置。以UTF-8编码方式为例: <%@ page …

    Java 2023年6月15日
    00
  • 浅析java异常栈

    下面我将为您详细讲解“浅析Java异常栈”的完整攻略。 浅析Java异常栈 异常栈的概述 在Java中,异常是指当前程序不能够继续执行下去的错误或问题。当程序发生异常时,会自动创建一个异常对象,并将这个异常对象抛出给Java虚拟机,Java虚拟机再根据异常对象调用相应的异常处理程序进行处理。 异常栈是异常处理机制的重要组成部分,它是一个由多个异常堆栈组成的数…

    Java 2023年5月27日
    00
  • 用于提取网易文件的hta代码

    为了提取网易文件,我们需要将其下载到本地。这个过程可以通过使用.hta文件来完成,它是一种HTML应用程序,用于在本地系统上运行脚本和批处理命令。本教程将向您展示如何利用.hta文件来提取网易云的音乐文件。 步骤一:新建HTA文件 我们先新建一个.hta文件,比如说我们把文件名命名为music.hta。然后在文件中输入以下代码。 <!DOCTYPE h…

    Java 2023年6月15日
    00
  • Java初学者问题图解(动力节点Java学院整理)

    对于“Java初学者问题图解(动力节点Java学院整理)”这个主题,我可以提供以下完整攻略: Java初学者问题图解 前言 Java作为一门广受欢迎的编程语言,吸引了许多初学者,但在学习过程中难免会遇到问题。本文就对Java学习过程中常见的问题进行了整理,并提供了图解和解决方法。 字符串问题 1. 字符串比较 问题描述:如何比较两个字符串是否相等? 问题分析…

    Java 2023年5月25日
    00
  • java连接SQL Server数据库的方法

    下面我将详细讲解Java连接SQL Server数据库的方法,包括如何配置环境、创建数据库连接、执行SQL语句等步骤。 环境配置 在Java中连接SQL Server数据库,需要先下载Microsoft JDBC Driver for SQL Server。可以前往Microsoft官方网站下载对应版本的驱动程序。另外,需要安装SQL Server数据管理工…

    Java 2023年5月19日
    00
  • Mybatis 中如何判断集合的size

    判断 Mybatis 中查询出来的结果集的 size 主要有以下几种方式: 判断查询结果是否为空 可以使用 Mybatis 自带的 isEmpty() 方法判断查询结果是否为空,与此相对地,还可以使用isNotEmpty() 方法判断结果是否不为空。例如: List<User> userList = userMapper.selectUserLi…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部