Spring学习笔记之Bean的循环依赖问题

news2024/11/13 14:39:02

文章目录

  • 什么是Bean的循环依赖
  • singleton下的set注入产生的循环依赖
  • prototype下的set注入产生的循环依赖
  • singleton下的构造注入产生的循环依赖
  • Spring解决循环循环的机理(面试题)

什么是Bean的循环依赖

A对象中有B属性。B对象中有A属性。这就是循环依赖。我依赖你,你也依赖我。

比如:丈夫类Husband,妻子类Wife。Husband中有Wife的引用。Wife中有Husband的引用。

//Husband.java
package com.powernode.spring6.bean;

public class Husband {
    private String name;
    private Wife wife;
}
//Wife.java
package com.powernode.spring6.bean;

public class Wife {
    private String name;
    private Husband husband;
}

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

//husband.java
package com.powernode.spring6.bean;

public class Husband {
    private String name;
    private Wife wife;

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

    public String getName() {
        return name;
    }

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

    // toString()方法重写时需要注意:不能直接输出wife,输出wife.getName()。要不然会出现递归导致的栈内存溢出错误。
    @Override
    public String toString() {
        return "Husband{" +
                "name='" + name + '\'' +
                ", wife=" + wife.getName() +
                '}';
    }
}
//Wife.java
package com.powernode.spring6.bean;

/**
 * @author 动力节点
 * @version 1.0
 * @className Wife
 * @since 1.0
 **/
public class Wife {
    private String name;
    private Husband husband;

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

    public String getName() {
        return name;
    }

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

    // toString()方法重写时需要注意:不能直接输出husband,输出husband.getName()。要不然会出现递归导致的栈内存溢出错误。
    @Override
    public String toString() {
        return "Wife{" +
                "name='" + name + '\'' +
                ", husband=" + husband.getName() +
                '}';
    }
}

<!-- spring.xml -->
<?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">

    <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>
//test
    @Test
    public void testSingletonAndSet(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Husband husbandBean = applicationContext.getBean("husbandBean", Husband.class);
        Wife wifeBean = applicationContext.getBean("wifeBean", Wife.class);
        System.out.println(husbandBean);
        System.out.println(wifeBean);
    }

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

<!-- spring.xml -->
<?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">

    <bean id="husbandBean" class="com.powernode.spring6.bean.Husband" scope="prototype">
        <property name="name" value="张三"/>
        <property name="wife" ref="wifeBean"/>
    </bean>
    <bean id="wifeBean" class="com.powernode.spring6.bean.Wife" scope="prototype">
        <property name="name" value="小花"/>
        <property name="husband" ref="husbandBean"/>
    </bean>
</beans>

程序出现错误:
创建名为husbandBean的bean时出错:请求的bean当前正在创建中:是否存在无法解析的循环引用


当循环依赖的所有的Bena的scope=“prototype”的时候,产生的循环依赖,Spring是无法解决的,会出现BeanCurrentlyInCreationException异常。

如果一个是singleton,一个是prototype,是没有问题的。

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

下面尝试的是singleton+构造注入的方式下,spring能否解决循环依赖

//Husband.java
package com.powernode.spring6.bean2;

public class Husband {
    private String name;
    private Wife wife;

    public Husband(String name, Wife wife) {
        this.name = name;
        this.wife = wife;
    }

    // -----------------------分割线--------------------------------
    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Husband{" +
                "name='" + name + '\'' +
                ", wife=" + wife +
                '}';
    }
}
//Wife.java
package com.powernode.spring6.bean2;

public class Wife {
    private String name;
    private Husband husband;

    public Wife(String name, Husband husband) {
        this.name = name;
        this.husband = husband;
    }

    // -------------------------分割线--------------------------------
    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Wife{" +
                "name='" + name + '\'' +
                ", husband=" + husband +
                '}';
    }
}
<!-- spring2.xml -->
<?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">

    <bean id="hBean" class="com.powernode.spring6.bean2.Husband" scope="singleton">
        <constructor-arg name="name" value="张三"/>
        <constructor-arg name="wife" ref="wBean"/>
    </bean>

    <bean id="wBean" class="com.powernode.spring6.bean2.Wife" scope="singleton">
        <constructor-arg name="name" value="小花"/>
        <constructor-arg name="husband" ref="hBean"/>
    </bean>
</beans>
//test
@Test
public void testSingletonAndConstructor(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring2.xml");
    Husband hBean = applicationContext.getBean("hBean", Husband.class);
    Wife wBean = applicationContext.getBean("wBean", Wife.class);
    System.out.println(hBean);
    System.out.println(wBean);
}

和上一个测试结果相同,都是提示产生了循环依赖,并且Sppring是无法解决这种循环依赖的。

主要原因是因为通过构造方法注入导致的:因为构造方法注入会导致实例化对象的过程和对象属性赋值的过程没有分离开,必须在一起完成导致的。

Spring解决循环循环的机理(面试题)

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

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

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

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

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

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


在这里插入图片描述
在以上类中包含了三个重要的属性:

  • Cache of singleton objects:bean name to bean instance.单例对象的缓存:key存储bean名称,value存储Bean对象(一级缓存)
  • Cache of early singleton objects: bean name to bean instance.早期单例对象的缓存:key存储bean名称,value存储早期的Bean对象(二级缓存)
  • Cache o singleton factories: bean name to ObjectFactory.单例工厂缓存:key存储bean名称,value存储该Bean对应的ObjectFactory对象(三级缓存)

这三个缓存其实本质上是三个Map集合。
在该类中有这样一个方法addSingletonFactory(),这个方法的作用是:将创建Bean对象的ObjecyFactory对象提前曝光。

在这里插入图片描述
在这里插入图片描述
从源码中可以看到,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/888311.html

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

相关文章

leetcode 1614.括号的最大嵌套深度

⭐️ 题目描述 &#x1f31f;leetcode链接&#xff1a;括号的最大嵌套深度 ps&#xff1a; 使用数据结构栈来存储 ( 在使用 maxDepth 变量记录栈顶 top 的最大值&#xff0c;当遇到 ) 时删除栈顶元素。举个例子 (1)((2))(((3)))&#xff0c;当遇到第一个 ( 时 top 1&#xff…

对dubbo的DubboReference.check的参数进行剖析

背景 在使用dubbo的时候&#xff0c;发现当消费者启动的时候&#xff0c;如果提供者没有启动&#xff0c;即使提供者后来启动了&#xff0c;消费者也调不通提供者提供的接口了。 注册中心使用都是nacos dubbo版本是3.0.4 例子 接口 public interface DemoService {String…

中期国际:外汇交易的利器:善用挂单技巧优化交易策略

在外汇交易中&#xff0c;挂单技巧是提高交易效率和灵活性的重要利器之一。善用限价单和止损单可以帮助交易者有效规避风险、控制入场点和出场点&#xff0c;从而提高交易效果。本文将介绍一些MT4挂单技巧&#xff0c;以帮助交易者优化交易策略&#xff0c;提高交易效率。 1. 了…

猿辅导设立“青少年科学探索基金”,鼓励天才少年投入科学研究

“少年智则国智&#xff0c;少年富则国富&#xff0c;少年强则国强。”国家发展离不开人才的培养。伴随我国进入高质量发展轨道&#xff0c;科学、人才、教育三位一体融合发展已经刻不容缓。我国基础学科人才紧缺成了不争的事实。目前&#xff0c;中国的GDP目前已是世界第二位&…

nginx创建和监听套接字分析

https://cloud.tencent.com/developer/article/1859856 简介 nginx作为一个web服务器&#xff0c;肯定是有listen套接字对外提供服务的&#xff0c;listen套接字是用于接收HTTP请求。 nginx监听套接字的创建是根据配置文件的内容来创建的&#xff0c;在nginx.conf文件中有…

视频音乐如何转换成mp3?教你超简单的转换方法

MP3文件通常比视频文件更小。因此&#xff0c;通过将音乐从视频中提取并转换为MP3格式&#xff0c;您可以更轻松地存储和传输它们。如果计划在手机或其他设备上存储音乐&#xff0c;转换为MP3格式可以帮助我们节省存储空间。而且&#xff0c;如果需要将音乐发送给朋友或上传到互…

基于JAVA高校校园点餐系统-lw+ppt

文章目录 前言一、主要技术javaMysql数据库JSP技术 二、系统设计1. 系统结构图 三、功能截图总结 前言 21世纪的今天&#xff0c;随着社会的发展与进步&#xff0c;人们对信息科学的认识已从低层次提升到高层次&#xff0c;从感性认识逐渐转变为理性认识。管理工作的重要性也逐…

新生录取查询系统怎么制作?

在制作新生录取查询系统前&#xff0c;先跟老师们介绍一下招生录取的详细流程&#xff0c;以便老师们更好的完成录取工作的筹备&#xff0c;顺利过渡招生季&#xff01; 1. 招生宣传和报名&#xff1a;学校通过各种途径进行招生宣传&#xff0c;向学生和家长介绍学校的特色、教…

图数据库_Neo4j学习cypher语言_常用函数_关系函数_字符串函数_聚合函数_数据库备份_数据库恢复---Neo4j图数据库工作笔记0008

然后再来看一些常用函数,和字符串函数,这里举个例子,然后其他的 类似 可以看到substring字符串截取函数 可以看到截取成功 聚合函数 这里用了一个count(n) 统计函数,可以看到效果 关系函数,我们用过就是id(r) 可以取出对应的r的id来这样..

北京影视展BIRTV 2023亮点提前盘点

2023年8月23-26日&#xff0c;“融合创新 面向未来”——由国家广播电视总局和中央广播电视总台共同指导&#xff0c;中国广播电视国际经济技术合作总公司主办的第三十届北京国际广播电影电视展览会&#xff08;BIRTV2023&#xff09;将在北京中国国际展览中心&#xff08;朝阳…

大股东被纪检监察调查,会否成为大牧人上市之路的又一拦路虎?

据悉&#xff0c;深圳证券交易所上市审核委员会已经定于2023年8月17日召开2023年第63次上市审核委员会审议会议&#xff0c;审核青岛大牧人机械股份有限公司&#xff08;即“大牧人”&#xff09;首发上市。这已经是大牧人因股东股权纷争&#xff08;见相关媒体报道&#xff09…

项目经理掌控项目进度的重要手段——甘特图

项目经理最担心的是无法了解团队成员每天的工作内容以及项目的进展情况。因此&#xff0c;每天的会议和项目周报是项目经理掌控项目进度的重要手段&#xff0c;能够帮助项目经理及时了解和跟踪项目的进展。 进度控制是指监督项目状态、更新项目进展、管理进度基准变更&#x…

Python Tkinter 树状浏览图,类和函数及文件浏览的应用(由idlelib tree模块修改)

模块由idlelib tree模块修改&#xff0c;完善一些问题&#xff0c;重写了获取类和函数的方法&#xff0c;便于获取正在编辑代码的类和函数。重写了文件浏览模块&#xff0c;支持添加收藏&#xff0c;双击py&#xff08;pyw&#xff09;文件会打开函数浏览器&#xff0c;文件浏览…

【AGC】崩溃数据消失问题

【问题背景】 最近有开发者集成了AGC的崩溃服务&#xff0c;出现了一个问题&#xff0c;在集成完成后&#xff0c;触发崩溃事件测试&#xff0c;在AGC后台可以看到当天崩溃的数据&#xff0c;但是启动次数显示为0。等到第二天再看数据时&#xff0c;连昨天的崩溃数据都没有了。…

一文带你搞懂MySQL的隔离级别

一. 前言 最近遇到这样一个题目&#xff1a;【假设目前你们使用的数据库是MySQL&#xff0c;现在有一个事务A&#xff0c;在事务A开始时读取数据的结果是1&#xff1b;事务A中间有一段耗时操作&#xff0c;在事务A中做耗时操作的同时&#xff0c;有另外一个事务B把数据值改成了…

MFC为控件添加背景图片

1、 添加选择Bitmap导入图片&#xff0c;图片文件最好放在项目res目录中&#xff0c;同时是BMP格式。上传后的图片在资源视图&#xff0c;命名为IDB_BITMAP_M_BACK。 2、在cpp的C***Dlg::OnPaint()函数下添加如下代码 void C***Dlg::OnPaint() {CPaintDC dc(this); // device…

录制游戏视频的软件有哪些?分享3款软件!

“有录制游戏视频的软件推荐吗&#xff1f;最近迷上了网游&#xff0c;想录制点自己高端操作的游戏画面&#xff0c;但是不知道用什么软件录屏比较好&#xff0c;就想问问大家&#xff0c;有没有好用的录制游戏视频软件。” 在游戏领域&#xff0c;玩家们喜欢通过录制游戏视频…

DevExpress WinForms数据编辑器组件,提供丰富的数据输入样式!(一)

DevExpress WinForms超过80个高影响力的WinForms编辑器和多用途控件&#xff0c;从屏蔽数据输入和内置数据验证到HTML格式化&#xff0c;DevExpress数据编辑库提供了无与伦比的数据编辑选项&#xff0c;包括用于独立数据编辑或用于容器控件(如Grid, TreeList和Ribbon)的单元格。…

二、编写第一个 Spring MVC 程序(总结项目报 404 问题以及 Spring MVC 的执行流程)

文章目录 一、编写第一个 Spring MVC 程序二、项目运行时报 404错误原因总结三、Spring MVC 的执行流程 一、编写第一个 Spring MVC 程序 创建 maven 项目&#xff0c;以此项目为父项目&#xff0c;在父项目的 pom.xml 中导入相关依赖 <dependencies><dependency…

Planning Poker

计划扑克 一人一副牌&#xff0c;投票表决&#xff0c;这个功能、故事点的工作量是多少 0&#xff1a;没有工作量 &#xff1f;&#xff1a;需求不清楚 -------------------------------------- 数字越大&#xff0c;工作量越大&#xff0c;越要细化 100 和 ∞ ----------…