【Spring】Spring循环依赖

目录

什么是循环依赖问题

循环依赖具体是怎么解决的

具体的解决步骤:

通俗实例:      

严谨的循环依赖解决图例

为什么使用的是三级缓存,二级缓存不够用吗?


什么是循环依赖问题

        Spring的循环依赖是指在Bean之间存在相互依赖关系,形成一个闭环的情况。简单来说,Bean A依赖于Bean B,同时Bean B也依赖于Bean A,这就构成了循环依赖。

        下面是一个示例,展示了Spring中循环依赖的情况:

// Class A
public class A {private B b;public A() {}public void setB(B b) {this.b = b;}public void doSomething() {System.out.println("Class A is doing something.");}
}// Class B
public class B {private A a;public B() {}public void setA(A a) {this.a = a;}public void doSomethingElse() {System.out.println("Class B is doing something else.");}
}

在上述示例中,类A依赖于类B的实例,而类B又依赖于类A的实例。当我们使用Spring容器来创建这两个类的实例时,就会发生循环依赖的情况。

<!-- XML 配置文件 -->
<bean id="a" class="com.example.A"><property name="b" ref="b" />
</bean><bean id="b" class="com.example.B"><property name="a" ref="a" />
</bean>

在上述配置中,我们定义了Bean A和Bean B,并通过属性注入方式使它们相互引用。

如果我们运行以下代码来获取Bean A的实例:

public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");A a = context.getBean(A.class);a.doSomething();
}

        在这种情况下,Spring会尝试创建Bean A的实例。但是由于循环依赖,它会先创建一个空的Bean A,并将其放入缓存中。然后,Spring继续创建依赖的Bean B,但因为它也依赖Bean A,Spring会从缓存中获取Bean A的实例,并将其注入到Bean B中。

        接下来,Spring回到缓存中,将属性注入到之前创建的Bean A实例中,完成Bean A的初始化。最后,Bean A的完全初始化实例返回给调用方。

循环依赖具体是怎么解决的

Spring使用了三级缓存以及"提前曝光"的机制来解决循环依赖的问题。

具体的解决步骤:

  1. 创建空对象:当Spring容器发现循环依赖时,会先创建一个空对象作为Bean A或Bean B的临时实例,并将其放入第一级缓存中。

  2. 属性注入:然后,Spring会继续创建被循环依赖的Bean的其他属性,并将这些属性注入到临时实例中。

  3. 提前曝光:在注入属性期间,如果发现循环依赖的Bean已经存在于第一级缓存中,Spring会将那个早期的实例提前曝光到第二级缓存中,以供其他需要依赖它的Bean使用。

  4. 创建完整实例:完成属性注入后,Spring会将临时实例传递给Bean的初始化方法,并执行初始化操作。初始化完成后,循环依赖的Bean就成为一个完整的可用实例了。

  5. 缓存实例:最后,Spring将这个完整的Bean实例放入第三级缓存中,以供后续使用。

通俗实例:      

        假设有两个人,小明和小红,他们互相依赖对方来完成一项任务。小明需要小红的帮助才能完成自己的任务,而小红也需要小明的帮助才能完成自己的任务。这就形成了一个循环依赖的情况。

为了解决这个问题,他们采取以下步骤:

  1. 小明向小红发出请求:小明首先联系小红,告诉她自己需要她的帮助来完成任务。

  2. 小红创建一个"空的"小明实例:小红收到请求后,会先创建一个空的小明实例,并进行标记,表示这是一个临时的、不完整的实例。

  3. 小红继续进行自己的任务:在创建小明实例的同时,小红并不会等待小明的实例完成,而是继续进行自己的任务。

  4. 小明提供帮助:在小红继续进行自己的任务期间,小明得到了小红的帮助,完成了自己的任务。

  5. 小红注入属性:当小明的任务完成后,小红会将小明实例中需要的属性注入进去,使得小明的实例成为一个完整的、可用的实例。

  6. 小红完成任务:接着,小红继续进行自己的任务,并顺利完成。

大概就是这样一个流程

严谨的循环依赖解决图例

 

为什么使用的是三级缓存,二级缓存不够用吗?

假设有两个类 A 和 B,它们相互依赖对方来完成初始化。A 类依赖于 B 类的实例,而 B 类也依赖于 A 类的实例。这种情况下,如果只使用二级缓存,会导致属性注入的顺序错误,从而无法正确解决循环依赖。

具体示例:

  1. 创建 A 类实例:首先,Spring 会尝试创建 A 类的实例,并将其放入二级缓存中。
  2. 创建 B 类实例:接下来,Spring 发现 B 类依赖 A 类的实例,于是尝试创建 B 类的实例,并将其放入二级缓存中。
  3. 属性注入:在属性注入过程中,由于 B 类的实例已经在二级缓存中,Spring 尝试从缓存中获取 B 类的实例,并将其注入到 A 类的实例中。但此时 A 类的实例还没有完全初始化,因此导致注入的 A 类实例不完整。
  4. 初始化 A 类:接着,Spring 继续初始化 A 类的实例并执行初始化操作,但由于 A 类实例的属性注入不完整,可能导致初始化失败或产生不正确的结果。
  5. 初始化 B 类:最后,Spring 继续初始化 B 类的实例,但由于 B 类实例依赖 A 类的实例,而 A 类实例又没有正确注入,导致 B 类的实例无法正确初始化。

由于二级缓存只能缓存已经完成初始化的 Bean,不能解决属性注入时的循环依赖问题。

        而三级缓存相比二级缓存能够正常应对循环依赖的原因在于它引入了一个"早期曝光"的机制,可以在属性注入之前提前暴露早期实例。

        具体来说,在三级缓存中,当检测到循环依赖时,Spring 会首先创建一个早期对象(Early Object),并将其放入三级缓存中。早期对象是一个未完成初始化的对象,其中的属性可能尚未注入完全。

通过三级缓存的机制,能够解决从二级缓存获取实例时属性注入顺序错误的问题。

具体流程:

  1. 创建 A 类早期对象:首先,Spring 创建 A 类的早期对象,并将其放入三级缓存中。

  2. 创建 B 类实例:接着,Spring 发现 B 类依赖 A 类的实例,于是尝试创建 B 类的实例,并将其放入二级缓存中。

  3. 属性注入:在属性注入过程中,Spring 从二级缓存中获取 B 类的实例,并将其注入到 A 类的早期对象中。虽然 A 类的早期对象的属性注入仍不完整,但关键是 B 类实例已经被注入。

  4. 完成 A 类实例化:接下来,Spring 继续完成 A 类的实例化,并执行初始化操作。由于 B 类实例已经注入,A 类能够正确地访问 B 类的属性。

  5. 创建 B 类早期对象:当初始化 A 类后,Spring 检测到 B 类也存在循环依赖,于是创建 B 类的早期对象,并将其放入三级缓存中。

  6. 属性注入:在属性注入过程中,Spring 从三级缓存中获取 B 类的早期对象,并将其注入到 A 类的实例中。这样,A 类实例的属性注入得以完成。

  7. 完成 B 类实例化:最后,Spring 继续完成 B 类的实例化,并执行初始化操作。由于 A 类实例已经注入,B 类能够正确地访问 A 类的属性。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Linux]进程概念

[Linux]进程概念 文章目录 [Linux]进程概念进程的定义进程和程序的关系Linux下查看进程Linux下通过系统调用获取进程标示符Linux下通过系统调用创建进程-fork函数使用 进程的定义 进程是程序的一个执行实例&#xff0c;是担当分配系统资源&#xff08;CPU时间&#xff0c;内存…

解决Pandas KeyError: “None of [Index([...])] are in the [columns]“问题

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

c++都补了c语言哪些坑?

目录 1.命名空间 1.1 定义 1.2 使用 2.缺省参数 2.1 概念 2.2 分类 3.函数重载 4.引用 4.1 概念 4.2 特性 4.3 常引用 4.4 引用和指针的区别 5.内联函数 1.命名空间 在 C/C 中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变量、函数和类的名称将…

windows下redis设置redis开机自启动方法(保姆级)

1.找到Redis所在的目录&#xff0c;在文件路径框中输入cmd: 2.进入到控制台下的Redis所在目录,输入下列命令: redis-server --service-install redis.windows-service.conf --loglevel verbose 3.找到redis.windows-service.conf文件,双击打开设置redis服务的密码: (不想设置密…

使用 Nacos 作为 Spring Boot 配置中心

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

【图像分类】基于卷积神经网络和主动学习的高光谱图像分类(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Ubuntu20.04搭建OpenGL环境(glfw+glad)

Ubuntu20.04搭建OpenGL环境(glfwglad) Linux环境搭建 本文在VMware安装Ubuntu20.04桌面版的环境下搭建OpenGL&#xff0c;按照本文搭建完成后可以执行LearnOpenGL网站上的demo。 关于VMware可自行到VMware Workstation Pro | CN下载 关于Ubuntu20.04桌面版可自行到官网或In…

k8s简介、虚拟机快速搭建k8s集群、集群管理方式及K8S工作原理和组件介绍

文章目录 1、k8s简介1.1、部署方式的变迁1.2、定义1.3、Kubernetes提供的功能 2、虚拟机快速搭建k8s集群2.1、虚拟机配置&#xff08;centos7 2G内存2个处理器&#xff09;2.2、基础环境准备2.3、docker安装&#xff08;易踩坑&#xff09;2.4、安装k8s组件2.5、master节点部署…

Python搭建http文件服务器实现手机电脑文件传输功能

第一种代码的界面如下&#xff1a;&#xff08;有缺点&#xff0c;中文乱码&#xff09; # !/usr/bin/env python3 # -*- coding:utf-8 _*-"""Simple HTTP Server With Upload. python -V3.6 This module builds on http.server by implementing the standard G…

版本控制工具Git集成IDEA的学习笔记(第一篇Gitee)

目录 一、Gitee的使用 1、注册网站会员 2、用户中心 3、创建远程仓库 4、配置SSH免密登录 二、集成IDEA&#xff0c;Git项目搭建 1、本地仓库搭建 1&#xff09;创建一个新项目 2&#xff09;打开终端&#xff0c;在当前目录新建一个Git代码库 3&#xff09;忽略文件 …

回归预测 | MATLAB实现BES-LSSVM秃鹰搜索算法优化最小二乘支持向量机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现BES-LSSVM秃鹰搜索算法优化最小二乘支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现BES-LSSVM秃鹰搜索算法优化最小二乘支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&a…

RabbitMQ笔记-RabbitMQ基本术语

RabbitMQ基本术语 相关概念; 生产者&#xff08;Producer&#xff09;&#xff1a;投递消息。消息&#xff1a;消息体&#xff08;payload&#xff09;标签&#xff08;label&#xff09;&#xff1b;生产者把消息交给rabbitmq&#xff0c;rabbitmq会根据标签把消息发给感兴趣…

SQL-每日一题【1587. 银行账户概要 II】

题目 表: Users 表: Transactions 编写解决方案, 报告余额高于 10000 的所有用户的名字和余额. 账户的余额等于包含该账户的所有交易的总和。 返回结果表单 无顺序要求 。 查询结果格式如下例所示。 示例 1&#xff1a; 解题思路 1.题目要求我们查询出额高于 10000 的所有…

使用高斯滤波器进行表面开放轮廓过滤研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【洁洁送书第五期】为什么我们要了解可观测性工程

导读 可观测性已成为一个热门话题&#xff0c;并广受关注。随着它的普及&#xff0c;“可观测性”不幸被误作“监控”或“系统遥测”的同义词。可观测性是软件系统的一个特征。而且&#xff0c;只有当团队采用新的实践进行持续开发时&#xff0c;才能在生产软件系统中有效利用这…

smiley-http-proxy-servlet 实现springboot 接口反向代理,站点代理,项目鉴权,安全的引入第三方项目服务

背景&#xff1a; 项目初期 和硬件集成&#xff0c;实现了些功能服务&#xff0c;由于是局域网环境&#xff0c;安全问题当时都可以最小化无视。随着对接的服务越来越多&#xff0c;部分功能上云&#xff0c;此时就需要有一种手段可以控制到其他项目/接口的访问权限。 无疑 反向…

API接口文档利器:Swagger 和 接口调试利器:Postman

2.接口相关工具 2.1API接口文档利器&#xff1a;Swagger 2.1.1Swagger介绍 Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务 (https://swagger.io/)。 它的主要作用是&#xff1a; 使得前后端分离开发更加方便&#xff0…

分类预测 | MATLAB实现S4VM半监督支持向量机二分类预测

分类预测 | MATLAB实现S4VM半监督支持向量机二分类预测 目录 分类预测 | MATLAB实现S4VM半监督支持向量机二分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 分类预测 | MATLAB实现S4VM半监督支持向量机二分类预测 程序设计 完整源码和数据获取方式&#xff1a; …

低压风机单片机方案

低压风机通常由电机、转子、机壳、进气管、出气管、齿轮和减速机等组成。电机带动转子旋转&#xff0c;旋转的转子带动齿轮和减速机转动&#xff0c;进而形成空气被吸入转子内部&#xff0c;通过旋转而产生的离心力把气体压缩&#xff0c;并将气体排出。 低压风机方案的主控型…

c++ 友元 运算符重载详解

友元 c是面向对象的&#xff0c;目的之一&#xff1a;封装 封装&#xff1a; 优点之一&#xff0c;就是安全。 缺点&#xff1a;在某些特殊的场合&#xff0c;不是很方便。 华为与IBM 40亿的咨询故事 IBM需要对华为各级部门做深度咨询分析&#xff0c; 为了提高咨询效率&a…