当前位置: 首页 > news >正文

九、Bean的循环依赖问题

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;}@Overridepublic 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;}@Overridepublic 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 {@Testpublic 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的实例,直接从缓存中获取即可。从而解决循环依赖问题。

相关文章:

九、Bean的循环依赖问题

1 什么是Bean的循环依赖 A对象中有B属性。B对象中有A属性。这就是循环依赖。我依赖你&#xff0c;你也依赖我。 比如&#xff1a;丈夫类Husband&#xff0c;妻子类Wife。Husband中有Wife的引用。Wife中有Husband的引用。 2 singleton下的set注入产生的循环依赖 丈夫类 pac…...

macOS关闭SIP后,仍无法修改/usr文件夹下文件

发现问题 MacOS 升级到Big Sur后&#xff0c;删除多余的python3文件&#xff0c;发现写不到磁盘&#xff0c;会报OSError: [Errno 30] Read-only file system的错误。经过了解&#xff0c;在Mac OS10.11 之后&#xff0c;苹果公司为了提高系统环境安全&#xff0c;引入了一个内…...

【编程基础之Python】8、Python复合数据类型

【编程基础之Python】8、Python复合数据类型Python复合数据类型列表&#xff08;List&#xff09;创建列表访问元素内置方法列表操作元组&#xff08;Tuple&#xff09;创建元组访问元素集合&#xff08;Set&#xff09;创建集合基本操作其他操作字典&#xff08;Dictionary&am…...

自动驾驶决策规划-控制方向学习资料总结(附相关资料的链接)

项目仓库 欢迎访问我的Github主页 项目名称说明chhCpp学习C仓库chhRobotics学习自动驾驶、控制理论相关仓库(python实现)chhRobotics_CPP学习自动驾驶、控制理论相关仓库(c实现)chhML 、chh-MachineLearning学习机器学习仓库chhRL学习强化学习仓库chhTricks存放一些有意思的t…...

网络安全岗位介绍——售前工程师

一、工作内容 1、独立完成并配合销售人员引导客户完成方案设计、产品选型、配置报价和能为客户提供安全咨询与方案优化等服务; 2、作为售前工程师&#xff0c;跟踪整个项目的进展&#xff0c;和销售进行配合&#xff0c;协调公司各种资源完成项目中标; 3、编写投标文件的技术…...

nodejs安装和卸载超详细步骤

安装程序①下载完成后&#xff0c;双击安装包&#xff0c;开始安装&#xff0c;使用默认配置安装一直点next即可&#xff0c;安装路径默认在C:\Program Files下&#xff0c;也可以自定义修改②安装路径默认在C:\Program Files下面&#xff0c;也能够自定义修改&#xff0c;而后…...

【Leetcode】移除链表元素 链表的中间节点 链表中倒数第k个节点

目录 一.【Leetcode203】移除链表元素 1.链接 2.题目再现 A.双指针法 B.类尾删法 C.哨兵位 二.【Leetcode876】链表的中间节点 1.链接&#xff1a;链表的中间节点 2.题目再现 3.解法&#xff1a;快慢指针 三.链表中倒数第k个节点 1.链接&#xff1a;链表中倒数第k个…...

快速上手配置firewalld

firewalld使用firewall-cmd命令配置策略。 查看当前firewalld当前服务运行状态 firewall-cmd --state firewalld防火墙状态还用使用如下命令查看状态 systemctl status firewalld 查看所有打开运行的端口 firewall-cmd --zonepublic --list-ports 查看区域信息情况 firewall…...

treap使用mt19937会导致问题原因分析

Treap 是一种使用随机数生成器来维护树形结构的数据结构&#xff0c;而 mt19937 是一种常用的伪随机数生成器。虽然 mt19937 可以生成高质量的随机数序列&#xff0c;但是在 Treap 中使用它可能会导致一些问题。 mt19937 返回的是一个 unsigned int 其中一个问题是&#xff0…...

tmux和vim

tmux 作用 分屏 允许断开Terminal连接后继续运行进程 结构 一个tmux可以开一堆session tmux: session 1, session 2, session 3 … Session: window 1, window 2, window 3… Window: pane 1, pane 2, pane 3… pane是最小单位&#xff0c;用shell语言编程 操作 输入…...

2023年全国最新保安员精选真题及答案12

百分百题库提供保安员考试试题、保安职业资格考试预测题、保安员考试真题、保安职业资格证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 121.《保安员证》是经由设区的&#xff08;&#xff09;单位进行发放。 A:市级人民政…...

Hbase的基本概念与架构

一、Hbase的概念 HBase是Hadoop的生态系统&#xff0c;是建立在Hadoop文件系统&#xff08;HDFS&#xff09;之上的分布式、面向列的数据库&#xff0c;通过利用Hadoop的文件系统提供容错能力。如果你需要进行实时读写或者随机访问大规模的数据集的时候&#xff0c;请考虑使用H…...

颠覆你的认知,业务同事都能开发软件,我简直无地自容……

经常看到网络鼓吹业务人员也能搭建应用&#xff0c;本是嗤之以鼻、半信半疑&#xff0c;但当这件事真实发生在自己身上时&#xff0c;竟觉得此言不虚&#xff1f; 一、背景 最近公司为了集成系统、提升扩展能力&#xff0c;引进了低代码平台JNPF&#xff0c;说个题外话&#…...

01 | n2n虚拟局域网

1 n2n简介 为了满足两个不同局域网的机器进行通信&#xff0c;让不同网段的机器能够进行P2P( 点对点 peer-to-peer ) 通信。2 n2n源码 https://github.com/ntop/n2n.git3 n2n名词 3.1 SuperNode 超级节点 SuperNode 相当与注册中心, 它会记录边缘节点的连接信息&#xff0c;…...

MFC界面控件BCGControlBar v33.4 - 支持Win 11 Mica material主题

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。BCGControlBar专业版和BCGSuite for MFC v33.4已正式发布了&#xff0c;该版本包含了对Windows 11 Mica materia…...

手把手教你用js实现手机通讯录功能(附源码)

js实现手机通讯录效果图需求需求一&#xff1a;锚点通过#id配合a标签使用css中scroll-behavior属性的使用需求二需求三获取汉字拼音的首字母方法1&#xff1a;使用插件&#xff0c;这里推荐pinyin-pro方法2&#xff1a;使用unicode去重数组中冗余的对象法一&#xff1a;用Map去…...

【C/C++】逗号表达式、算术运算符优先级

一、逗号表达式 1、如下图中代码&#xff0c;为变量d赋值&#xff0c;d的值为逗号表达式中的哪一个呢&#xff1f; 运行结果&#xff1a;d的值为6 2、再举个例子 运行结果&#xff1a;d的结果还是6 3、再举个例子 运行结果 以上面三种不同的逗号表达式为例&#xff0c;…...

携禾生物面试总结

面试时间&#xff1a; 2022年2月3日 1.项目C11的特性具体有用到哪些&#xff1f; 智能指针 lambda表达式 auto unordered_map 2.智能指针用到了哪几种智能指针 3.shared_ptr和weak_ptr区别 4.多线程实现方式 prosix线程》pthread windows的_beginthreaex MFC多线程 ACEM中…...

FPGA纯verilog手写HDMI发送IP 提供源码和技术支持

目录1、前言2、设计思路和框架TMDS 编码算法OSERDESE串并转换3、顶层源码和IP封装4、源码和IP获取1、前言 本设计使用Xilinx原语和自己手写的代码实现了HDMI发送功能&#xff0c;纯verilog手写&#xff0c;有源码&#xff0c;也提供封装好的IP&#xff0c;你喜欢用例化的方式就…...

【知识点】OkHttp 原理 8 连问

前言OkHttp可以说是Android开发中最常见的网络请求框架,OkHttp使用方便&#xff0c;扩展性强&#xff0c;功能强大&#xff0c;OKHttp源码与原理也是面试中的常客但是OKHttp的源码内容比较多&#xff0c;想要学习它的源码往往千头万绪&#xff0c;一时抓不住重点.本文从几个问题…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义&#xff08;Task Definition&…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...