Bean的循环依赖问题

2023.11.10

        通俗来讲,循环依赖指的是一个实例或多个实例存在相互依赖的关系(类之间循环嵌套引用)。比如:丈夫类Husband,妻子类Wife。Husband中有Wife的引用。Wife中有Husband的引用。

        正常调用这两对象不会出现问题,但是被 Spring 容器管理后的对象就会出现循环依赖问题。

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

        下面先测试一下在singleton+setter的模式下产生的循环依赖,Spring是否能够解决。

定义相互依赖的两个类husband和wife:

package spring6.bean;

public class husband {
    private String name;
    private wife wife;

    public void setName(String name) {
        this.name = name;
    }

    public void setWife(spring6.bean.wife wife) {
        this.wife = wife;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "husband{" +
                "name='" + name + '\'' +
                ", wife=" + wife.getName() +
                '}';
    }
}
package spring6.bean;

public class wife {
    private String name;
    private husband husband;

    public void setName(String name) {
        this.name = name;
    }

    public void setHusband(spring6.bean.husband husband) {
        this.husband = husband;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "wife{" +
                "name='" + name + '\'' +
                ", husband=" + husband.getName() +
                '}';
    }
}

在xml文件配置:

    <bean id="husbandBean" class="spring6.bean.husband" scope="singleton">
        <property name="name" value="艾弗森"/>
        <property name="wife" ref="wifeBean"/>
    </bean>

    <bean id="wifeBean" class="spring6.bean.wife" scope="singleton">
        <property name="name" value="卡戴珊"/>
        <property name="husband" ref="husbandBean"/>
    </bean>

测试程序:

    @Test
    public void testSingletonAndSet(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("cycle-depend.xml");
        husband husbandBean = applicationContext.getBean("husbandBean", husband.class);
        wife wifeBean = applicationContext.getBean("wifeBean",wife.class);
        System.out.println(husbandBean);
        System.out.println(wifeBean);
    }

 运行结果:

通过测试得知:在singleton + set注入的情况下,循环依赖是没有问题的。Spring可以解决这个问题。

        根据bean的生命周期可以知道,spring容器当中的bean是先被实例化的 实例化之后才是属性初始化,所以当husband需要给wife属性赋值的时候 wife这个bean已经提前曝光了而且还是单例的,所以可以解决循环依赖问题。

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

再来测试一下prototype+set注入的方式下,循环依赖会不会出现问题,只需要将配置文件的两个singleton都改为prototype即可。 运行测试程序:

发现结果会报:BeanCreationException 异常

再次测试,发现如果只改一个singleton为prototype,也可以正常运行。

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

测试一下singleton + 构造注入的方式下,spring是否能够解决这种循环依赖。

不放代码了,直接说结论:不行。

因为构造方法注入会导致实例化对象的过程对象属性赋值的过程没有分离开,这两过程是同时进行的,而之前set注入中,两个过程是分离开的,这也是set注入能解决循环依赖的根本原因。

Spring解决循环依赖的机理

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

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

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

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

总结Spring只能解决setter方法注入的单例bean之间的循环依赖。

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

Spring的三级缓存


    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
​
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

​

一级缓存:存储的是完整的单例Bean对象,这个Bean对象已经赋值过了。

二级缓存:存储的是早期的单例Bean对象,这个Bean对象属性还没有赋值。

三级缓存:存储的是单例工厂对象,每一个单例Bean对象都会对应一个单例工厂对象。

        spring会先从一级缓存中获取Bean,如果获取不到,则从二级缓存中获取Bean,如果二级缓存还是获取不到,则从三级缓存中获取之前曝光的ObjectFactory对象,通过ObjectFactory对象获取Bean实例,这样就解决了循环依赖的问题。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python实现全向轮EKF_SLAM

python实现全向轮EKF_SLAM 代码地址及效果运动预测观测修正参考算法 代码地址及效果 代码地址 运动预测 简化控制量 u t u_t ut​ 分别定义为 v x Δ t v_x \Delta t vx​Δt&#xff0c; v y Δ t v_y \Delta t vy​Δt&#xff0c;和 ω z Δ t \omega_z \Delta t ωz…

解压游戏资源,导出游戏模型

游戏中有很多好看的角色&#xff0c;地图等等资源。 你有没有想过&#xff0c;把他们导出到自己的游戏中进行魔改又或则玩换肤等操作呢&#xff1f; 相信很多同学都喜欢拳皇中的角色&#xff0c; 那么我们今天就拿拳皇15举例子&#xff0c;导出他的资源。 首先要先安装好这个…

通过商品ID获取到京东商品详情页面数据,京东商品详情官方开放平台API接口,京东APP详情接口,可以拿到sku价格,销售价演示案例

淘宝SKU详情接口是指&#xff0c;获取指定商品的SKU的详细信息。SKU是指提供不同的商品参数组合的一个机制&#xff0c;通过不同的SKU来标识商品的不同组合形式&#xff0c;如颜色、尺寸等。SKU详情接口可以帮助开发者获取指定商品的SKU列表&#xff0c;以及每个SKU的属性、库存…

算法:穷举,暴搜,深搜,回溯,剪枝

文章目录 算法基本思路例题全排列子集全排列II电话号码和字母组合括号生成组合目标和组合总和优美的排列N皇后有效的数独解数独单词搜索黄金矿工不同路径III 总结 算法基本思路 穷举–枚举 画出决策树设计代码 在设计代码的过程中&#xff0c;重点要关心到全局变量&#xff…

ChatGPT风潮再起!最新国内产品一网打尽,畅游指南曝光!

一、国内类chatgpt产品 在人工智能领域&#xff0c;自然语言处理&#xff08;NLP&#xff09;是一个重要的方向&#xff0c;涉及到语音识别、文本生成、机器翻译、问答系统等多个应用场景。近年来&#xff0c;随着深度学习技术的发展&#xff0c;NLP也取得了突破性的进展&#…

React向组件内部动态传入带内容的结构--props

children props&#xff1a;通过组件标签体传入结构 <A><B>xxx</B> </A> {this.props.children}render props&#xff1a;通过组件标签属性传入结构&#xff0c;一般用render函数属性 <A render{data> <C data{data}></C>}></…

super() 和 super(props) 有什么区别?

一、ES6 类 在 ES6 中&#xff0c;通过 extends 关键字实现类的继承&#xff0c;方式如下&#xff1a; class sup { constructor(name) { this.name name; } printName() { console.log(this.name); }}class sub extends sup { constructor(name, age) { …

二十二、W5100S/W5500+RP2040树莓派Pico<SMTP发送邮件>

文章目录 1 前言2 简介2 .1 什么是SMTP&#xff1f;2.2 SMTP是如何工作的&#xff1f;2.3 SMTP、IMAP和POP32.4 SMTP应用场景 3 WIZnet以太网芯片4 SMTP发送邮件示例概述以及使用4.1 流程图4.2 准备工作核心4.3 连接方式4.4 主要代码概述4.5 结果演示 5 注意事项6 相关链接 1 前…

常见产品结构四大类型 优劣势比较

一般&#xff0c;我们通过产品架构来构建用户体验&#xff0c;这样可以提供更清晰的导航和组织、优化用户流程和交互、增强产品的可扩展性和可维护性&#xff0c;提升用户的满意度和忠诚度。如果没有明确的产品结构&#xff0c;可能会导致功能冗余或功能缺失、交互流程混乱等问…

「Verilog学习笔记」使用generate…for语句简化代码

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 分析 generate…for语句是Verilog HDL语言特有的语句&#xff0c;使用循环结构编写可综合的多个形式相近的代码&#xff0c;循环变量必须由特定关键字genvar声明。 timesca…

VSD Viewer v6.16.1(Visio绘图文件阅读器)

VSD Viewer是一款可以打开和查看Microsoft Visio文件的工具&#xff0c;适用于Windows和macOS操作系统。它具有以下优点&#xff1a; 直观易用&#xff1a;VSD Viewer的用户界面非常简单直观&#xff0c;易于使用。支持多种文件格式&#xff1a;VSD Viewer支持多种Visio文件格…

二维码解码器怎么用?快速分解二维码图片的方法

现在很多人会将链接网址生成二维码之后来使用&#xff0c;这种方式能够让别人更快的获取链接的内容&#xff0c;而且扫码访问内容的方式也更适合大家的使用习惯。那么如果想要获取二维码中的链接时&#xff0c;一般会使用二维码解码器来处理&#xff0c;那么具体该怎么使用呢&a…

驱动基石之_tasklet中断下半部_工作队列_中断线程化处理

tasklet中断下半部 linux的中断分为两个部分&#xff1a; 1.中断上半部&#xff1a;在中断上半部期间&#xff0c;不允许被其他中断打断&#xff0c;直到中断上半部的服务函数执行完。 2.中断下半部&#xff1a;中断下半部&#xff0c;在执行中断下半部服务函数的期间&#xf…

断点续传-http中Header参数Range(分段请求基础)

文章目录 Range请求头信息介绍RangeIf-Range 响应头Content-RangeAccept-Ranges 需要用到几个http头 rangeif-rangecontent-rangeaccept-range 断点续传的优缺点 好处&#xff1a;防止大文件下载过程出现网络异常&#xff0c;而前功尽弃。缺点&#xff1a;要发起多次请求&…

echarts 圆环图 高亮事件 切换 中心文字

createEcharts() {let chartDom this.$refs.echartsthis.Echarts echarts.init(chartDom)let option {title: {text: 128, //主标题文本subtext: 总数, //副标题文本left: center,top: 32%,textStyle: {fontFamily: Montserrat-MediumItalic,fontSize: 30,color: #fff,align…

构建全面预算体系,加强企业风险管理

全面预算管理体系是帮助企业实现其战略目标的重要手段。随着预算管理理念备受重视&#xff0c;这种新型的企业管理模式通过高效科学的方式和工具&#xff0c;在我国新时代背景下&#xff0c;逐渐成为了企业经营运作过程中针对挑战的有效措施。通常情况下&#xff0c;企业将全面…

Vue、fabricJS 画布实现自由绘制折线

作者GitHub&#xff1a;https://github.com/gitboyzcf 有兴趣可关注 Vue3代码&#xff0c;Vue2相似改吧改吧 前言 Fabric.js Fabric.js&#xff08;英文官网&#xff09;是一个强大而简单的 Javascript HTML5画布库&#xff08;也就是针对canvas进行的封装操作&#xff0c;使…

Java设计模式-创建者模式-工厂模式

工厂模式 工厂模式简单工厂模式工厂方法模式抽象工厂模式 工厂模式 要求&#xff1a;由一个特定的工厂提供所需的对象&#xff0c;由工厂来完成对象的创建 工厂模式一般分为三种&#xff1a;简单工厂模式&#xff0c;工厂方法模式&#xff0c;抽象工厂模式 其中简单工厂模式不…

SSL证书优惠购买,HTTPS证书双11价格

SSL证书优惠购买哪里有&#xff1f;如何申请HTTPS证书&#xff1f;就看沃通2023“双十一限时特惠”活动&#xff0c;精选HTTPS证书、国密HTTPS证书年度优惠好价&#xff0c;单域名SSL证书、通配符SSL证书任您选择&#xff01;沃通优惠价格、服务优势&#xff0c;访问沃通CA官网…

为什么说软文推广中了解用户是关键?

数字化时代下软文成为众多企业推广品牌的方式之一&#xff0c;所谓软文&#xff0c;就是指以向用户提供信息&#xff0c;并将产品隐含在信息中的柔性手段。 想要使软文效果明显&#xff0c;就必须深入了解用户&#xff0c;把握其需求、兴趣和行为特点&#xff0c;这也是今天媒…
最新文章