九、Bean的循环依赖问题

news2024/11/25 6:18:48

1 什么是Bean的循环依赖

A对象中有B属性。B对象中有A属性。这就是循环依赖。我依赖你,你也依赖我。
比如:丈夫类Husband,妻子类Wife。Husband中有Wife的引用。Wife中有Husband的引用。
在这里插入图片描述

2 singleton下的set注入产生的循环依赖

丈夫类

package com.powernode.spring6.bean;
/**
 * 丈夫类
 */
public class Husband {
    private String name;
    private Wife wife;

    public void setName(String name) {
        this.name = name;
    }
    public void setWife(Wife wife) {
        this.wife = wife;
    }
    public String getName() {
        return name;
    }
    @Override
    public String toString() {
        return "Husband{" +
                "name='" + name + '\'' +
                ", wife=" + wife.getName() +
                '}';
    }
}

妻子类

package com.powernode.spring6.bean;
/**
 * 妻子类
 */
public class Wife {
    private String name;
    private Husband husband;

    public void setName(String name) {
        this.name = name;
    }
    public void setHusband(Husband husband) {
        this.husband = husband;
    }
    public String getName() {
        return name;
    }
    @Override
    public String toString() {
        return "Wife{" +
                "name='" + name + '\'' +
                ", husband=" + husband.getName() +
                '}';
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--singleton + setter 模式下的循环依赖 没有问题-->
    <bean id="husbandBean" class="com.powernode.spring6.bean.Husband" scope="singleton">
        <property name="name" value="张三"/>
        <property name="wife" ref="wifeBean"/>
    </bean>
    <bean id="wifeBean" class="com.powernode.spring6.bean.Wife" scope="singleton">
        <property name="name" value="李四"/>
        <property name="husband" ref="husbandBean"/>
    </bean>
</beans>
package com.powernode.spring6.test;

import com.powernode.spring6.bean.Husband;
import com.powernode.spring6.bean.Wife;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class CircularDependencyTest {
    @Test
    public void testCD(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Husband husbandBean = applicationContext.getBean("husbandBean", Husband.class);
        System.out.println(husbandBean);
        Wife wifeBean = applicationContext.getBean("wifeBean", Wife.class);
        System.out.println(wifeBean);
    }
}

在这里插入图片描述

singleton + setter 模式下的循环依赖 没有问题

singleton表示在整个Spring容器当中是单例的,独一无二的对象

在singleton + setter模式下,为什么循环依赖不会出现问题,Spring是如何应对的?

主要的原因是,在这种模式下Spring对Bean的管理主要分为清晰的两个阶段:

第一个阶段:在Spring容器加载的时候,实例化Bean,只要其中任意一个Bean实例化之后,马上进行 “曝光”【不等属性赋值就曝光】
第二个阶段:Bean“曝光”之后,再进行属性的赋值(调用set方法。)。

核心解决方案是:实例化对象和对象的属性赋值分为两个阶段来完成的。
注意:只有在scope是singleton的情况下,Bean才会采取提前“曝光”的措施。

3 prototype下的set注入产生的循环依赖

在prototype + setter模式下的循环依赖,存在问题,会出现异常!

BeanCurrentlyInCreationException 当前的Bean正在处于创建中异常。。。

注意:当两个bean的scope都是prototype的时候,才会出现异常。如果其中任意一个是singleton的,就不会出现异常。

4 singleton下的构造注入产生的循环依赖

和上面测试结果相同,都是提示产生了循环依赖,并且Spring是无法解决这种循环依赖的。
为什么呢?
主要原因是因为通过构造方法注入导致的:因为构造方法注入会导致实例化对象的过程和对象属性赋值的过程没有分离开,必须在一起完成导致的。

5 Spring解决循环依赖的机理

Spring为什么可以解决set + singleton模式下循环依赖?

根本的原因在于:这种方式可以做到将“实例化Bean”和“给Bean属性赋值”这两个动作分开去完成。

实例化Bean的时候:调用无参数构造方法来完成。此时可以先不给属性赋值,可以提前将该Bean对象“曝光”给外界。

给Bean属性赋值的时候:调用setter方法来完成。

两个步骤是完全可以分离开去完成的,并且这两步不要求在同一个时间点上完成。

也就是说,Bean都是单例的,我们可以先把所有的单例Bean实例化出来,放到一个集合当中(我们可以称之为缓存),所有的单例Bean全部实例化完成之后,以后我们再慢慢的调用setter方法给属性赋值。这样就解决了循环依赖的问题。

在Spring框架底层源码级别上是如何实现的呢?
在这里插入图片描述
DefaultSingletonBeanRegistry类中包含三个重要的属性:

  • private final Map<String, Object> singletonObjects. 单例对象的缓存:key存储bean名称,value存储Bean对象【一级缓存】
    一级缓存存储的是:单例Bean对象。完整的单例Bean对象,也就是说这个缓存中的Bean对象的属性都已经赋值了。是一个完整的Bean对象。
  • private final Map<String, Object> earlySingletonObjects. 早期单例对象的缓存:key存储bean名称,value存储早期的Bean对象【二级缓存】
    二级缓存存储的是:早期的单例Bean对象。这个缓存中的单例Bean对象的属性没有赋值。只是一个早期的实例对象。
  • private final Map<String, ObjectFactory<?>> singletonFactories . 单例工厂缓存:key存储bean名称,value存储该Bean对应的ObjectFactory对象【三级缓存】
    三级缓存存储的是:单例工厂对象。这个里面存储了大量的“工厂对象”,每一个单例Bean对象都会对应一个单例工厂对象。 这个集合中存储的是,创建该单例对象时对应的那个单例工厂对象。

这三个缓存其实本质上是三个Map集合。

在该类中有这样一个方法addSingletonFactory(),这个方法的作用是:将创建Bean对象的ObjectFactory对象提前曝光。
在这里插入图片描述
再分析下面的源码:
在这里插入图片描述
从源码中可以看到,spring会先从一级缓存中获取Bean,如果获取不到,则从二级缓存中获取Bean,如果二级缓存还是获取不到,则从三级缓存中获取之前曝光的ObjectFactory对象,通过ObjectFactory对象获取Bean实例,这样就解决了循环依赖的问题。

总结:
Spring只能解决setter方法注入的单例bean之间的循环依赖。ClassA依赖ClassB,ClassB又依赖ClassA,形成依赖闭环。Spring在创建ClassA对象后,不需要等给属性赋值,直接将其曝光到bean缓存当中。在解析ClassA的属性时,又发现依赖于ClassB,再次去获取ClassB,当解析ClassB的属性时,又发现需要ClassA的属性,但此时的ClassA已经被提前曝光加入了正在创建的bean的缓存中,则无需创建新的的ClassA的实例,直接从缓存中获取即可。从而解决循环依赖问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/385240.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RabbitMQ的使用

1.初识MQ1.1.同步和异步通讯微服务间通讯有同步和异步两种方式&#xff1a;同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。两种方式各有优劣&#xff0c;打电话可以立即得到响应&#xff0c;但是你却不…

Python3,好看的外(shen)表(cai)千篇一律,炫彩的日志万里挑一。

炫彩日志输出1、引言2、代码实战2.1 库介绍2.2 库安装2.3 代码示例2.3.1 demo2.3.2 实战3、总结1、引言 小屌丝&#xff1a;鱼哥&#xff0c; 我今天被炫到了。 小鱼&#xff1a;怎么了&#xff0c;你还能被旋到了&#xff1f; 小屌丝对啊&#xff0c; 被炫到了&#xff0c;很…

python SDK打包

什么是SDK 先放一段专业性的文字来解释&#xff1a; SDK即“软体开发工具包”&#xff0c;一般是一些被软件工程师用于为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具的集合。通俗点是指由第三方服务商提供的实现软件产品某项功能的工具包。 开发者不…

【PyTorch】教程:torch.nn.ELU

torch.nn.ELU CLASS torch.nn.ELU(alpha1.0, inplaceFalse) paper: Fast and Accurate Deep Network Learning by Exponential Linear Units (ELUs). 参数 alpha ([float]) – α\alphaα 默认为1.0inplace ([bool] ) – 内部做, 默认为 False ELU 定义 ELU(x){x,if x>0α…

与流程挖掘布道者熵评科技孙一鸣博士共话流程挖掘市场的起源与前景 | 爱分析访谈

调研&#xff1a;李进宝 陈元新 撰写&#xff1a;李进宝 陈元新 随着数字化转型持续深入&#xff0c;国内企业流程挖掘需求初露端倪。流程挖掘是指通过采集和分析企业数据&#xff0c;以可视化流程图还原企业实际发生的业务流程&#xff0c;进而评估流程运行状况、诊断流程运…

Spring 6 and JUnit 5 组合

Spring 6 and JUnit 5 组合 Spring 6 and JUnit 5 只需引入相关的包&#xff0c;不过偶尔可能会出现 no tests were found&#xff0c;最后有解决方案。 引入相关依赖包 <dependencies><dependency><groupId>jakarta.annotation</groupId><artif…

边界无限入选首届“网络安全高成长性企业”并荣获“勇创之星”

近日&#xff0c;由工业和信息化部、四川省人民政府主办的“2023年中国网络和数据安全产业高峰论坛网络安全产融合作分论坛”在成都举行&#xff0c;论坛上公布了“2022年度网络安全高成长性企业”名单。云原生安全、应用安全“灵动智御”理念创领者北京边界无限科技有限公司&a…

密码算法(SM1、SM2、SM3、SM4、同态加密、密态计算、隐私计算和安全多方计算)

文章目录SM1 对称密码SM2 椭圆曲线公钥密码算法SM3 杂凑算法SM4 对称算法同态加密密态计算和隐私计算安全多方计算技术安全多方计算的应用场景对称加密算法非对称加密算法&#xff08;公钥加密&#xff09;参考文章SM1、SM2、SM3和SM4 为了保障商用密码的安全性&#xff0c;国家…

HTTP#1 请求数据格式和响应数据格式

一. 简介概念Hyper Text Transfer Protocol (HTTP), 超文本传输协议, 规定了浏览器和服务器之间数据传输的规则HTTP协议特点1.基于TCP协议:面向连接&#xff0c;安全2.基于请求-响应模型的:一次请求对应一次响应3. HTTP协议是无状态的协议: 对于事务处理没有记忆能力, 每次请求…

JAVA中公平锁和非公平锁有什么区别?

从公平的角度来说,Java 中的锁总共可分为两类:公平锁和非公平锁。但公平锁和非公平锁有哪些区别? 正文 公平锁:每个线程获取锁的顺序是按照线程访问锁的先后顺序获取的,最前面的线程总是最先获取到锁。非公平锁:每个线程获取锁的顺序是随机的,并不会遵循先来先得的规则…

深眸科技|机器视觉提升制造性能,焕发传统企业智造新活力!

随着机器视觉技术的成熟与发展&#xff0c;其在工业制造中得到越来越广泛的应用。机器视觉在工业制造领域的应用朝着智能识别、智能检测、智能测量以及智能互联的完整智能体系方向发展。此外&#xff0c;快速变化的市场需求&#xff0c;不断涌入行业的竞争对手&#xff0c;让传…

操作系统真相还原_第8章:内存管理系统

文章目录8.1 Makefile 简介makefile基本语法make参数伪目标自定义变量与系统变量隐含规则自动化变量与模式规则8.2 实现assert断言操作系统代码编译、链接并写入磁盘启动bochs执行物理内存使用情况8.3 实现字符串操作函数操作系统代码编译、链接并写入磁盘启动bochs执行物理内存…

C++基础(一)—— C++概述、C++对C的扩展(作用域、struct类型、引用、内联函数、函数默认参数、函数占位参数、函数重载)

1. C概述1.1 c简介“c”中的来自于c语言中的递增运算符&#xff0c;该运算符将变量加1。c起初也叫”c withclsss”.通过名称表明&#xff0c;c是对C的扩展&#xff0c;因此c是c语言的超集&#xff0c;这意味着任何有效的c程序都是有效的c程序。c程序可以使用已有的c程序库。为什…

Halcon数据结构

1.HTuple类型 1、既可以表示的类型有int&#xff0c;double、float、string&#xff0c;既可以表示单个值&#xff1b; 2、可以是容器&#xff1b; 3、可以是数组&#xff0c;数组遍历时需要有下标&#xff0c;如&#xff1a;变量名称[下标] 图像数据类型 Byte&#xff1a;8…

药房管理系统;药库管理系统

第一&#xff0c;主要功能&#xff1a;  本系统集日常销售、药品进销存、会员积分、GSP管理等药店所需的所有功能于一体&#xff0c;实现店铺管理的全部自动化。第二、新功能&#xff1a;  增加了“按功能查询药品”的功能&#xff0c;使软件用户可以根据客户的症状推荐合适…

【进阶】2、搭建K8s集群【v1.23】

[toc] 一、安装要求 在开始之前&#xff0c;部署Kubernetes集群机器需要满足以下几个条件&#xff1a; 一台或多台机器&#xff0c;操作系统 CentOS7.x-86_x64硬件配置&#xff1a;2GB或更多RAM&#xff0c;2个CPU或更多CPU&#xff0c;硬盘30GB或更多集群中所有机器之间网络…

编写SPI_Master驱动程序_老方法

编写SPI_Master驱动程序 文章目录编写SPI_Master驱动程序参考资料&#xff1a;一、 SPI驱动框架1.1 总体框架1.2 怎么编写SPI_Master驱动1.2.1 编写设备树1.2.2 编写驱动程序二、 编写程序2.1 数据传输流程2.2 写代码致谢参考资料&#xff1a; 内核头文件&#xff1a;include\…

数字IC手撕代码--联发科(总线访问仲裁)

题目描述当A、B两组的信号请求访问某个模块时&#xff0c;为了保证正确的访问&#xff0c;需要对这些信号进行仲裁。请用Verilog实现一个仲裁器&#xff0c;对两组请求信号进行仲后&#xff0c;要求&#xff1a;协议如图所示&#xff0c;请求方发送req&#xff08;request&…

数据推荐 | 人体行为识别数据集

人体行为识别任务旨在通过对人体姿态进行分析&#xff0c;识别出人体的具体动作&#xff0c;为人体行为预测、突发事件处理、智能健身、智能看护等领域提供技术支持。 图片 图片 人体行为识别数据标注方式 人体行为数据通用的标注方式包括人体关键点标注和动作标签标注&#…

Spring Boot 整合分布式缓存 Memcached

Memcached是一个开源、高性能&#xff0c;将数据分布于内存中并使用key-value存储结构的缓存系统。它通过在内存中缓存数据来减少向数据库的频繁访问连接的次数&#xff0c;可以提高动态、数据库驱动之类网站的运行速度。 Memcached在使用是比较简单的&#xff0c;在操作上基本…