06-解决Spirng中的循环依赖问题

news2024/11/15 15:52:15

Bean的循环依赖问题

循环依赖: A对象中有B属性 , B对象中有A属性(丈夫类Husband中有Wife的引用, 妻子类Wife中有Husband的引用)

在这里插入图片描述

toString()方法重写时直接输出wife/husband会出现递归导致的栈内存溢出错误

  • 直接输出wife/husband会调用它们的toString()方法, 在toString()方法又会调用husband/wife的toString()方法一直循环
//丈夫类
public class Husband {
    private String name;
    private Wife wife;
   	// 属性的setter方法

    // toString()方法重写时直接输出wife会出现递归导致的栈内存溢出错误,需要输出wife.getName()
    @Override
    public String toString() {
        return "Husband{" +
                "name='" + name + '\'' +
                ", wife=" + wife.getName() +
                '}';
    }
}
//妻子类
public class Wife {    
    private String name;
    private Husband husband;
    //属性的setter方法

    // toString()方法重写时不能直接输出husband,需要输出husband.getName()
    @Override
    public String toString() {
        return "Wife{" +
                "name='" + name + '\'' +
                ", husband=" + husband.getName() +
                '}';
    }
}

singleton下的set注入

Spring可以解决在singleton+setter模式下出现的循环依赖问题, 因为在这种模式下Spring对Bean的管理主要分为清晰的两个阶段

  • 实例化对象阶段:Spring容器实例化Bean的时候会先创建对象 , 不等其属性赋值就会曝光加入正在创建的bean的缓存中 , 这样其他bean就可以直接引用它
  • 对象的属性赋值阶段Bean曝光之后,再调用set方法进行属性的赋值
  • 注意: 只有在scope是singleton的情况下,Bean才会采取提前曝光的措施 , 多实例下不会曝光
<?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表示在整个Spring容器当中是单例的,独一无二的对象-->
    <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> 
public class CircularDependencyTest {
    @Test
    public void testSingletonAndSet(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Husband husbandBean = applicationContext.getBean("husbandBean", Husband.class);
        Wife wifeBean = applicationContext.getBean("wifeBean", Wife.class);
        //Husband{name="张三",wife=小花}
        System.out.println(husbandBean);
        //Wife{name="小花",husband=张三}
        System.out.println(wifeBean);
    }
}

prototype下的set注入

若循环依赖的所有Bean的scope="prototype"时 , Spring无法解决它们产生的循环依赖问题,此时会出现BeanCurrentlyInCreationException异常

  • 当两个bean的scope都是prototype的时候才会出现Bean正在处于创建中异常 , 如果其中任意一个是singleton的就不会出现异常
<?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">
    <!--由于wifeBean/husbandBean是多实例的,只有每一次执行getBean()方法的时候才会创建新的Bean,故会造成死循环-->
    <!--ref相当于从容器中手动获取对象,由于其是多实例的,所以每次获取的都是新对象-->
    <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>
    
     <!--因为wifeBean是单实例的会提前曝光,所以husbandBean可以new新对象-->
    <bean id="husbandBean" class="com.powernode.spring6.bean.Husband" scope="prototype">
        <property name="name" value="张三"/>
        <property name="wife" ref="wifeBean"/>
    </bean>
    <!--当两个bean的scope都是prototype的时候才会出现异常,如果其中任意一个是singleton的就不会出现异常-->
    <bean id="wifeBean" class="com.powernode.spring6.bean.Wife" scope="singleton">
        <property name="name" value="小花"/>
        <property name="husband" ref="husbandBean"/>
    </bean>
</beans>

singleton下的构造注入

Spring无法解决singleton下的构造注入产生的循环依赖,会出现BeanCurrentlyInCreationException异常

  • 因为构造方法注入会导致实例化对象的过程和对象属性赋值的过程没有分离开导致循环依赖, 想要实例化对象必须完成对象属性的赋值
//丈夫类
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 +
                '}';
    }
}
//妻子类
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 +
                '}';
    }
}
<?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>

Spring解决循环依赖的原理

Spring解决set + singleton模式下循环依赖的问题是将实例化Bean给Bean属性赋值这两个动作分开去完成(这两步不要求在同一个时间点上完成)

  • 先实例化单实例的Bean: 通过调用无参数构造方法时把所有的单例Bean实例化出来,放到一个Map集合(缓存) 当中曝光给外界
  • 然后给Bean属性赋值:调用setter方法来完成对象的属性赋值

Spring框架底层源码级别的缓存实现

  • 完整单例对象的缓存:key存储bean名称,value存储的单例Bean对象属性都已经赋值【一级缓存】
  • 早期单例对象的缓存:key存储bean名称,value存储早期的Bean对象属性没有赋值【二级缓存】
  • 单例工厂对象的缓存:key存储bean名称,value存储每个Bean对象对应的ObjectFactory单例工厂对象【三级缓存】

在这里插入图片描述

AbstractAutowireCapableBeanFactory类的doCreateBean()方法完成Bean的创建和属性赋值

  • creatBeanInstance只会创建Bean对象, addSingletonFactory方法中将bean对象缓存起来然后曝光, populateBean方法填充bean即给bean的属性赋值

  • DefaultSingletonBeanRegistry的addSingletonFactory()方法的作用是将创建Bean对象的ObjectFactory工厂对象提前曝光

在这里插入图片描述

spring会先从一级缓存中获取Bean,获取不到则从二级缓存中获取Bean,如果还是获取不到则从三级缓存中获取之前曝光的ObjectFactory对象,然后通过ObjectFactory对象获取Bean实例并放到二级缓存

在这里插入图片描述

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

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

相关文章

Halcon WPF 开发学习笔记(3):WPF+Halcon初步开发

文章目录 前言在MainWindow.xaml里面导入Halcon命名空间WPF简单调用Halcon创建矩形简单调用导出脚本函数 前言 本章会简单讲解如何调用Halcon组件和接口&#xff0c;因为我们是进行混合开发模式。即核心脚本在平台调试&#xff0c;辅助脚本C#直接调用。 在MainWindow.xaml里面…

【哈夫曼树的构造】

文章目录 如何构造哈夫曼树哈夫曼树构造算法的实现 如何构造哈夫曼树 哈夫曼算法口诀&#xff1a; 1.构造森林全是根&#xff1b;2.选用两小造新树&#xff1b; 3.删除两小添新人&#xff1b;4.重复2,3剩单根&#xff1b; 例&#xff1a;有4个新结点a,b,c,d&#xff0c;权值为…

数据结构 | 图

最小生成树算法 Prime算法 算法思路&#xff1a;从已选顶点所关联的未选边中找出权重最小的边&#xff0c;并且生成树不存在环。 其中&#xff0c;已选顶点是构成最小生成树的结点&#xff0c;未选边是不属于生成树中的边。 例子&#xff1a; 第一步&#xff1a; 假设我们从顶…

第十八章DOM操作控制

DOM操作分类&#xff1a; jQuery中的DOM操作 一。设置和获取样式值 1.追加样式 addClass&#xff1a;追加CSS效果 removeClass&#xff1a;去除CSS内容 2.切换样式 3.判断是否含有指定样式&#xff1a;判断的是布尔类型的值 二。内容操作&#xff1a; 1.HTML代码操作 2.TEXT代…

C++二分查找算法:132 模式

说明 本篇是视频课程的讲义&#xff0c;可以看直接查看视频。也可以下载源码&#xff0c;包括空源码。 题目 给你一个整数数组 nums &#xff0c;数组中共有 n 个整数。132 模式的子序列 由三个整数 nums[i]、nums[j] 和 nums[k] 组成&#xff0c;并同时满足&#xff1a;i &l…

【每日一题】—— B. Deja Vu(Codeforces Round 907 (Div. 2))(暴力枚举、队列)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

2023.11.13-istio之故障注入流量拆分流量镜像熔断-oss

istio之故障注入&流量拆分&流量镜像&熔断 目录 文章目录 istio之故障注入&流量拆分&流量镜像&熔断目录本节实战1、故障注入注入 HTTP 延迟故障&#x1f6a9; 实战&#xff1a;注入 HTTP 延迟故障-2023.11.12(测试成功) 注入 HTTP abort 故障&#x1f6…

【算法】繁忙的都市(Kruskal算法)

题目 城市C是一个非常繁忙的大都市&#xff0c;城市中的道路十分的拥挤&#xff0c;于是市长决定对其中的道路进行改造。 城市C的道路是这样分布的&#xff1a; 城市中有 n 个交叉路口&#xff0c;编号是 1∼n &#xff0c;有些交叉路口之间有道路相连&#xff0c;两个交叉…

【案例】超声波测距系统设计

1.1 总体设计 1.1.1 概述 学习了明德扬至简设计法和明德扬设计规范&#xff0c;本人用FPGA设计了一个测距系统。该系统采用超声波进行测量距离再在数码管上显示。在本案例的设计过程中包括了超声波的驱动、三线式数码管显示等技术。经过逐步改进、调试等一系列工作后&#xf…

通信世界扫盲基础二(原理部分)

上次我们刚学习了关于通信4/G的组成和一些通识&#xff0c;今天我们来更深层次了解一些原理以及一些新的基础~ 目录 专业名词 LTE(4G系统) EPC s1 E-UTRAN UE UU X2 eNodeB NR(5G系统) NGC/5GC NG NG-RAN Xn gNodeB N26接口 手机的两种状态 空闲态 连接态 …

HCIP-双点双向重发布

实验拓扑 要求&#xff1a;全网没有次优路径、且尽量负载均衡。 设备配置 R1 [V200R003C00] #sysname R1 #snmp-agent local-engineid 800007DB03000000000000snmp-agent #clock timezone China-Standard-Time minus 08:00:00 # portal local-server load flash:/portalpage.…

RT-DETR算法改进:更换损失函数DIoU损失函数,提升RT-DETR检测精度

💡本篇内容:RT-DETR算法改进:更换损失函数DIoU损失函数 💡本博客 改进源代码改进 适用于 RT-DETR目标检测算法(ultralytics项目版本) 按步骤操作运行改进后的代码即可🚀🚀🚀 💡改进 RT-DETR 目标检测算法专属 文章目录 一、DIoU理论部分 + 最新 RT-DETR算法…

【数据库开发】DataX开发环境的安装部署(Python、Java)

文章目录 1、简介1.1 DataX简介1.2 DataX功能1.3 支持的数据通道 2、DataX安装配置2.1 DataX2.2 Java2.3 Python 3、DataX Web安装配置3.1 mysql3.2 DataX Web3.2.1 简介3.2.2 架构图3.2.3 依赖环境3.2.4 安装 4、入门使用4.1 DataX自带打印示例测试4.2 DataX生成任务模板文件4…

配置开启Docker2375远程连接与解决Docker未授权访问漏洞

一、配置开启Docker远程连接 首先需要安装docker,参考我这篇文章&#xff1a;基于CentOS7安装配置docker与docker-compose 配置开启Docker远程连接的步骤&#xff1a; //1-编辑/usr/lib/systemd/system/docker.service 文件 vim /usr/lib/systemd/system/docker.service //2…

Linux socket编程(1):套接字、字节序和地址结构体

套接字(socket)是一种使用标准Unix文件描述符与其他程序进行通信的方式&#xff0c;它在实际的应用中都十分常用。所以从这一篇文章开始&#xff0c;我将详细介绍一下Linux环境下的socket的用法。本篇文章将介绍套接字、字节序和地址结构体的相关知识。 文章目录 1 什么是套接字…

深入理解指针(一)

目录 内存和地址 内存 如何理解编址 指针变量和地址 取地址操作符&#xff08;&&#xff09; 指针变量和解引用操作符&#xff08;*&#xff09; 指针变量 如何拆解指针类型 解引用操作符 指针变量的大小 ​编辑 指针变量类型的意义 指针的解引用 指针-整…

sass 封装媒体查询工具

背景 以往写媒体查询可能是这样的&#xff1a; .header {display: flex;width: 100%; }media (width > 320px) and (width < 480px) {.header {height: 50px;} }media (width > 480px) and (width < 768px) {.header {height: 60px;} }media (width > 768px) …

【Java】定时任务 - Timer/TimerTask 源码原理解析

一、背景及使用 日常实现各种服务端系统时&#xff0c;我们一定会有一些定时任务的需求。比如会议提前半小时自动提醒&#xff0c;异步任务定时/周期执行等。那么如何去实现这样的一个定时任务系统呢&#xff1f; Java JDK提供的Timer类就是一个很好的工具&#xff0c;通过简单…

【Qt】撤销/恢复的快捷键

使用Qt的时候&#xff0c;有时需要撤销修改的代码&#xff0c;但可能回撤过头了。 下面提供2个快捷键&#xff0c;当撤销过头时&#xff0c;可恢复撤销内容。 撤销的快捷键是 CtrlZ 恢复/向前的快捷键是 CtrlShiftZ 我们可以自定义快捷键。 点击【工具】->【选项】 点击…

基于XML的声明式事务

场景模拟 参考基于注解的声明式事务 修改Spring的配置文件 将Spring配置文件中去掉tx:annotation-driven标签&#xff0c;并添加配置&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org…