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

详解Spring的循环依赖问题、三级缓存解决方案源码分析

0、基础:Bean的生命周期

        在Spring中,由于IOC的控制反转,创建对象不再是简单的new出来,而是交给Spring去创建,会经历一系列Bean的生命周期才创建出相应的对象。而循环依赖问题也是由Bean的生命周期过程导致的问题,因此我们首先需要了解Bean的生命周期。

Bean的生命周期可以概括为4步:

实例化----属性注入----初始化----销毁

详细的讲,步骤如下:

        ******************************************实例化123*******************************************

  1. 定位:Spring容器会根据配置文件(如XML、注解等)或编程式配置来确定需要创建的Bean。
  2. 加载:Spring容器会加载配置文件并解析其中的Bean定义,将其转换为内部数据结构,例如BeanDefinition。

  3. 实例化:在实例化阶段,Spring容器会根据Bean定义中的信息创建Bean的实例。这个过程可以通过构造函数实例化、工厂方法实例化或者通过反射机制来实现。*****************************************属性注入4*********************************************

  4. 属性注入:在实例化Bean之后,Spring容器会对Bean进行属性注入。这可以通过setter方法注入、构造函数参数注入或字段注入等方式来完成。
    ****************************************初始化5~9********************************************

  5. Aware接口回调:如果Bean实现了Spring的Aware接口,容器会通过回调方式将一些特殊的资源注入到Bean中。例如,如果Bean实现了BeanFactoryAware接口,容器会将当前的BeanFactory实例注入到Bean中。

  6. 初始化前回调:如果Bean实现了InitializingBean接口,容器会在初始化之前调用它的afterPropertiesSet()方法,给Bean一个机会执行一些初始化操作。同时,Spring容器还支持使用自定义的初始化方法。

  7. 初始化后回调:如果Bean配置了初始化回调方法,容器会调用该方法进行一些自定义的初始化逻辑处理。

  8. 后置处理器(BeanPostProcessor):Spring容器会调用注册的Bean后置处理器对Bean进行加工和增强。例如,可以通过AOP技术在这个阶段为Bean动态生成代理对象。

  9. 完成:至此,Bean已经成功创建,并且已经完成了所有的初始化过程。此时可以将Bean提供给其他对象使用。
    ****************************************销毁10、11******************************************

  10. 销毁前回调(PreDestroy):在容器关闭之前,调用Bean的销毁前回调方法,执行一些清理操作和释放资源的任务。

  11. 销毁:容器关闭时,销毁所有Bean实例,包括调用相应Bean的销毁方法,进行最终的清理和资源释放。

 1、循环依赖问题

        例如下面的代码,A和B类就构成了循环依赖,原因如下:

@Component
public class A {@Autowiredprivate B b;
}
@Component
public class B{@Autowiredprivate A a;
}

创建Bean的步骤: 

  1. Spring 扫描 class 得到 BeanDefinition;
  2. 根据得到的 BeanDefinition 去生成 bean;
  3. 首先根据 class 推断构造方法;
  4. 根据推断出来的构造方法,反射,得到一个对象(我们称为原始对象);
  5. 填充原始对象中的属性(依赖注入);
  6. 如果原始对象中的某个方法被 AOP 了,那么则需要根据原始对象生成一个代理对象;
  7. 把最终生成的代理对象放入单例池(源码中叫做 singletonObjects)中,下次 getBean 时就直接从单例池拿即可;

        对于上述步骤的第4步, 得到原始对象后需要注入属性,A 类中存在一个 B 类的 b 属性,此时就会根据 b 属性的类型和属性名去 BeanFactory 中去获取 B 类所对应的单例bean
        如果此时 B 类在 BeanFactory 中还没有生成对应的 Bean,那么就需要去生成,就会经过 B 的 Bean 的生命周期,也就会同样的,需要A类的Bean,就发生了循环依赖,导致A和B的bean都创建不出来。

 

        概括而言:
        A Bean创建–>依赖了 B 属性–>触发 B Bean创建—>B 依赖了 A 属性—>需要 A Bean(但A Bean还在创建过程中)

        然而实际上,Spring通过三级缓存的方式自动解决了这个问题 。

2、三级缓存的引入

2.1 非AOP情况下的解决方案

         根据上文的分析我们发现,出现循环依赖的根本原因,是B的Bean需要注入A属性的时候,Bean A还没有创建出来,导致的。那么相应的,只要:
        在进行依赖注入之前,先把 A 的原始 Bean 放入缓存提早暴露,只要放到缓存了,其他 Bean 需要时就可以从缓存中拿了,这个缓存就应该是earlySingletonObjects),放入缓存后,再进行依赖注入。
        由于提前暴露,在创建B的Bean过程中,当需要注入A的属性时,就可以从缓存中拿到A提前暴露的原始对象(还不是最终Bean),就解决了问题。关键在于全程只有一个A的原始对象,其后续的生命周期没有变化。
        如下图所示:

   2.2 三级缓存具体     

        因此,对于不同时期的Bean,如原始Bean、完整周期的Bean,需要不同的缓存来存放,底层源码中有三级缓存: 

/** Cache of singleton objects: bean name –> bean instance */
private final Map singletonObjects = new ConcurrentHashMap(256);/** Cache of singleton factories: bean name –> ObjectFactory */
private final Map> singletonFactories = new HashMap>(16);/** Cache of early singleton objects: bean name –> bean instance */
private final Map earlySingletonObjects = new HashMap(16);
  • 一级缓存:singletonObjects;缓存的是已经经历了完整生命周期的bean对象。
  • 二级缓存:earlySingletonObjects;比 singletonObjects 多了一个 early ,表示缓存的是早期的 bean对象(原始对象)。早期指的是 Bean 的生命周期还没走完就把这个 Bean 放入了 earlySingletonObjects
  • 三级缓存:singletonFactories;缓存的是 ObjectFactory,表示对象工厂,用来创建某个对象的。

3、有AOP情况下使用singletonFactories 

3.1 引入三级缓存

        看似我们只需要1、2级缓存就能够解决问题了,为什么需要三级缓存呢?
        这就需要考虑到AOP代理对象的问题了:

        上文的红字提到,之所以能够提前暴露,是因为假定的A的原始对象始终是同一个对象,但如果有AOP的情况下呢?我们考虑这样的场景:

         按照上文的分析,假设创建B的bean过程中,注入了A的原始对象属性。然后,A的原始对象采用AOP产生了一个代理对象,即,A的Bean变成了 AOP 之后的代理对象。而B中的 属性a对应的并不是 AOP 之后的代理对象,而仍然是原始对象。

        也就是说,这种情况下,B 依赖的 A 和最终的 A 不是同一个对象!

        而解决这个问题的方法,就是引入三级缓存的 singletonFactories 

 3.2 三级缓存具体解析

        实际上,在有AOP的情况下,Spring并没有像第2节所说,直接将示例缓存到二级缓存,而是生成完原始对象之后”多此一举“地将实例先封装到objectFactory中,在需要引用的时候再通过singletonFactory.getObject()获取。

        跟进getObject()方法,其实执行了getEarlyBeanReference这个关键方法。

    this.addSingletonFactory(beanName, () -> {return this.getEarlyBeanReference(beanName, mbd, bean);});

         也就是说,Spring将当前bean缓存到earlyProxyReferences中,标识提前曝光的bean。而wrapIfNecessary是用于Spring AOP自动代理的,也就是说在被提前引用前,进行了AOP代理,并得到了代理对象。
        此时earlySingletonObjects 缓存中的对象就是代理对象了!

        因此,假设此时有其他对象依赖了A,就可以从earlySingletonObjects中获取到A原始对象的代理对象了,并且和A是同一个对象,实现了目标。

3.3 后续依赖问题

        当 B 创建完了之后,A 继续进行生命周期,而 A 在完成属性注入后,会按照它本身的逻辑去进行AOP,而此时我们知道 A 原始对象已经经历过了 AOP ,所以对于 A 本身而言,不会再去进行 AOP了,那么怎么判断一个对象是否经历过了 AOP 呢?

         注意postProcessAfterInitialization方法,会当前beanName是否在earlyProxyReferences中,如果在就AOP过了,不在则执行AOP方法。

         此时对于Bean A对象而言已经完成创建了,可以把它放入缓存singletonObjects中了,因此从earlySingletonObjects 中得到代理对象,然后入 singletonObjects 中。

        至此,整个循环依赖解决完毕。

4、总结 

        这里引用看到的一篇写的很清晰的博客http://t.csdn.cn/GeHkA的图来说明具体流程:

Spring AOP循环依赖 

         对于三级缓存的singletonFactories,总结而言

        缓存的是一个 ObjectFactory ,主要用来去生成原始对象进行了 AOP之后得到的「代理对象」。
        在每个 Bean 的生成过程中,都会提前暴露一个工厂,这个工厂可能用到,也可能用不到:
(1)如果没有出现循环依赖依赖本 bean,那么这个工厂无用,本 bean 按照自己的生命周期执行,执行完后直接把本 bean 放入 singletonObjects 中即可(对应本文章的第1节)
(2)如果出现了循环依赖依赖了本 bean,则:
        <2.1>如果有 AOP 的话,另外那个 bean 执行 ObjectFactory 提交得到一个 AOP 之后的代理对象。(对应本文章第3节)
        <2.2>如果无需 AOP ,则直接得到一个原始对象。(对应本文章第2节)

相关文章:

详解Spring的循环依赖问题、三级缓存解决方案源码分析

0、基础&#xff1a;Bean的生命周期 在Spring中&#xff0c;由于IOC的控制反转&#xff0c;创建对象不再是简单的new出来&#xff0c;而是交给Spring去创建&#xff0c;会经历一系列Bean的生命周期才创建出相应的对象。而循环依赖问题也是由Bean的生命周期过程导致的问题&#…...

oracle分析函数学习

0、建表及插入测试数据 --CREATE TEST TABLE AND INSERT TEST DATA. create table students (id number(15,0), area varchar2(10), stu_type varchar2(2), score number(20,2));insert into students values(1, 111, g, 80 ); insert into students values(1, 111, j, 80 ); …...

代码随想录训练营day17|110.平衡二叉树 257. 二叉树的所有路径 404.左叶子之和 v...

TOC 前言 代码随想录算法训练营day17 一、Leetcode 110.平衡二叉树 1.题目 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a; 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。 示例 1&#x…...

C# Thread用法

C# 中的线程&#xff08;Thread&#xff09;是一种并发执行的机制&#xff0c;允许同时执行多个代码块&#xff0c;从而提高程序的性能和响应性。下面是关于如何使用 C# 线程的一些基本用法&#xff1a; 1. 创建线程&#xff1a; 使用 System.Threading 命名空间中的 Thread 类…...

新榜 | CityWalk本地生活商业价值洞察报告

如果说现在有人问&#xff0c;最新的网络热词是什么? “CityWalk”&#xff0c;这可能是大多数人的答案。 近段时间&#xff0c;“CityWalk”刷屏了各种社交媒体&#xff0c;给网友们带来了一场“城市漫步”之旅。 脱离群体狂欢&#xff0c;这个在社交媒体引发热议的词汇背后又…...

LVS负载均衡集群-NAT模式部署

集群 集群&#xff1a;将多台主机作为一个整体&#xff0c;然后对外提供相同的服务 集群使用场景&#xff1a;高并发的场景 集群的分类 1.负载均衡器集群 减少响应延迟&#xff0c;提高并发处理的能力 2&#xff0c;高可用集群 增强系统的稳定性可靠性&…...

C++学习笔记总结练习:effective 学习日志

准则 1.少使用define define所定义的常量会在预处理的时候被替代&#xff0c;出错编译器不容易找到错误。而且还没有作用范围限制&#xff0c;推荐使用constdefine宏定义的函数&#xff0c;容易出错&#xff0c;而且参数需要加上小括号&#xff0c;推荐使用inline有的类中例如…...

Vue教程(五):样式绑定——class和style

1、样式代码准备 样式提前准备 <style>.basic{width: 400px;height: 100px;border: 1px solid black;}.happy{border: 4px solid red;background-color: rgba(255, 255, 0, 0.644);background: linear-gradient(30deg, yellow, pink, orange, yellow);}.sad{border: 4px …...

开放网关架构演进

作者&#xff1a;庄文弘&#xff08;弘智&#xff09; 淘宝开放平台是阿里与外部生态互联互通的重要开放途径&#xff0c;通过开放的产品技术把阿里经济体一系列基础服务&#xff0c;像水、电、煤一样输送给我们的商家、开发者、社区媒体以及其他合作伙伴&#xff0c;推动行业的…...

torch一些操作

Pytorch文档 Pytorch 官方文档 https://pytorch.org/docs/stable/index.html pytorch 里的一些基础tensor操作讲的不错 https://blog.csdn.net/abc13526222160/category_8614343.html 关于pytorch的Broadcast,合并与分割,数学运算,属性统计以及高阶操作 https://blog.csd…...

ICCV23 | Ada3D:利用动态推理挖掘3D感知任务中数据冗余性

​ 论文地址&#xff1a;https://arxiv.org/abs/2307.08209 项目主页&#xff1a;https://a-suozhang.xyz/ada3d.github.io/ 01. 背景与动因 3D检测(3D Detection)任务是自动驾驶任务中的重要任务。由于自动驾驶任务的安全性至关重要(safety-critic)&#xff0c;对感知算法的延…...

软件工程模型-架构师之路(四)

软件工程模型 敏捷开发&#xff1a; 个体和交互 胜过 过程和工具、可以工作的软件 胜过 面面俱到的文件、客户合作胜过合同谈判、响应变化 胜过 循序计划。&#xff08;适应需求变化&#xff0c;积极响应&#xff09; 敏捷开发与其他结构化方法区别特点&#xff1a;面向人的…...

ubuntu20.04共享文件夹—— /mnt/hgfs里没有共享文件夹

参考文章&#xff1a;https://blog.csdn.net/Edwinwzy/article/details/129580636 虚拟机启用共享文件夹后&#xff0c;/mnt/hgfs下面为空&#xff0c;使用 vmware-hgfsclient 查看设置的共享文件夹名字也是为空。 解决方法&#xff1a; 1. 重新安装vmware tools. 在菜单…...

Redis中的有序集合及其底层跳表

前言 本文着重介绍Redis中的有序集合的底层实现中的跳表 有序集合 Sorted Set Redis中的Sorted Set 是一个有序的无重复值的集合&#xff0c;他底层是使用压缩列表和跳表实现的&#xff0c;和Java中的HashMap底层数据结构&#xff08;1.8&#xff09;链表红黑树异曲同工之妙…...

js 小程序限流函数 return闭包函数执行不了

问题&#xff1a; 调用限流 &#xff0c;没走闭包的函数&#xff1a; checkBalanceReq&#xff08;&#xff09; loadsh.js // 限流 const throttle (fn, context, interval) > {console.log(">>>>cmm throttle", context, interval)let canRun…...

【数据结构】堆的初始化——如何初始化一个大根堆?

文章目录 源码是如何插入的&#xff1f;扩容向上调整实现大根堆代码&#xff1a; 源码是如何插入的&#xff1f; 扩容 在扩容的时候&#xff0c;如果容量小于64&#xff0c;那就2倍多2的扩容&#xff1b;如果大于64&#xff0c;那就1.5倍扩容。 还会进行溢出的判断&#xff0c…...

【韩顺平 零基础30天学会Java】程序流程控制(2days)

day1 程序流程控制&#xff1a;顺序控制、分支控制、循环控制 顺序控制&#xff1a;从上到下逐行地执行&#xff0c;中间没有任何判断和跳转。 Java中定义变量时要采用合法的前向引用。 分支控制if-else&#xff1a;单分支、双分支和多分支。 单分支 import java.util.Scann…...

从入门到精通Python隧道代理的使用与优化

哈喽&#xff0c;Python爬虫小伙伴们&#xff01;今天我们来聊聊如何从入门到精通地使用和优化Python隧道代理&#xff0c;让我们的爬虫程序更加稳定、高效&#xff01;今天我们将对使用和优化进行一个简单的梳理&#xff0c;并且会提供相应的代码示例。 1. 什么是隧道代理&…...

19万字智慧城市总体规划与设计方案WORD

导读&#xff1a;原文《19万字智慧城市总体规划与设计方案WORD》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 感知基础设施 感知基础设施架构由感知范围、感知手…...

[赛博昆仑] 腾讯QQ_PC端,逻辑漏洞导致RCE漏洞

简介 !! 内容仅供学习,请不要进行非法网络活动,网络不是法外之地!! 赛博昆仑是国内一家较为知名的网络安全公司&#xff0c;该公司今日报告称 Windows 版腾讯 QQ 桌面客户端出现高危安全漏洞&#xff0c;据称“黑客利用难度极低、危害较大”&#xff0c;腾讯刚刚已经紧急发布…...

python Requests

Requests概述 官方文档&#xff1a;http://cn.python-requests.org/zh_CN/latest/,Requests是python的HTTP的库&#xff0c;我们可以安全的使用 Requests安装 pip install Requests -i https://pypi.tuna.tsinghua.edu.cn/simple Requests的使用 Respose的属性 属性说明url响…...

【深入解析:数据结构栈的魅力与应用】

本章重点 栈的概念及结构 栈的实现方式 数组实现栈接口 栈面试题目 概念选择题 一、栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。栈中的数…...

安卓机显示屏的硬件结构

显示屏的硬件结构 显示屏的硬件结构主要由背光源、液晶面板和驱动电路构成。可以将液晶面板看成一个三明治的结构&#xff0c;即在两片偏振方向互相垂直的偏光片系统中夹着一层液晶层。自然光源通过起偏器&#xff08;偏光片之一&#xff09;后&#xff0c;变成了垂直方向的偏…...

基于swing的超市管理系统java仓库库存进销存jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 基于swing的超市管理系统 系统有3权限&#xff1a;管…...

常用系统命令

重定向 cat aa.txt > bbb.txt 将输出定向到bbb.txt cat aaa.txt >> bbb.txt 输出并追加查看进程 ps ps -ef 显示所有进程 例⼦&#xff1a;ps -ef | grep mysql |&#xff1a;管道符 kill pid 结束进程&#xff0c; 如 kill 3732&#xff1b;根据进程名结束进程可以先…...

【Spring专题】Spring之Bean生命周期源码解析——阶段四(Bean销毁)(拓展,了解就好)

目录 前言阅读建议 课程内容一、Bean什么时候销毁二、实现自定义的Bean销毁逻辑2.1 实现DisposableBean或者AutoCloseable接口2.2 使用PreDestroy注解2.3 其他方式&#xff08;手动指定销毁方法名字&#xff09; 三、注册销毁Bean过程及方法详解3.1 AbstractBeanFactory#requir…...

配置Docker,漏洞复现

目录 配置Docker 漏洞复现 配置Docker Docker的配置在Linux系统中相对简单&#xff0c;以下是详细步骤&#xff1a; 1.安装Docker&#xff1a;打开终端&#xff0c;运行以下命令以安装Docker。 sudo apt update sudo apt install docker.io 2.启动Docker服务&#xff1a;运…...

微信小程序 游戏水平评估系统的设计与实现_pzbe0

近年来&#xff0c;随着互联网的蓬勃发展&#xff0c;游戏公司对信息的管理提出了更高的要求。传统的管理方式已无法满足现代人们的需求。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;随着各行业的不断发展&#xff0c;使命召…...

moba登录不进去提示修改问题问题解决方式

问题&#xff1a; 安装moba后&#xff0c;运行时运行不起来&#xff0c;提示输入密码&#xff0c;安装、卸载多个版本都不行 方法&#xff1a; 使用ResetMasterPassword工具进行重置主密码 官网下载地址&#xff1a; MobaXterm Xserver and tabbed SSH client - resetmaster…...

Unsafe upfileupload

文章目录 client checkMIME Typegetimagesize 文件上传功能在web应用系统很常见&#xff0c;比如很多网站注册的时候需要上传头像、上传附件等等。当用户点击上传按钮后&#xff0c;后台会对上传的文件进行判断 比如是否是指定的类型、后缀名、大小等等&#xff0c;然后将其按…...

长沙网站制作公司有哪些/seo学堂

【摘要】PHP作为一种超文本预处理器&#xff0c;已经成为了我们常用的网站编程语言&#xff0c;并且结合了C语言&#xff0c;Java等我们常见的编程语言&#xff0c;所以&#xff0c;有很多web开发领域的新人都看中了他的使用广泛性&#xff0c;有很多人都想了解php的内容&#…...

自媒体自助下单网站怎么做/网络推广方式有哪几种

2019独角兽企业重金招聘Python工程师标准>>> 一、 Order deny,allow //先看deny语句 deny from all allow from 127.0.0.1 //先禁止全部&#xff0c;后允许127.0.0.1 二、 Order allow&#xff0c;deny //先看allow语句 deny from all allow from 127.0…...

百度快速收录网站/搜外网

在redhat下安装MySQL&#xff0c;步骤如下Mysql目录安装位置&#xff1a;/usr/local/mysql数据库保存位置&#xff1a;/data/mysql日志保存位置&#xff1a;/data/log/mysql下载安装包 http://downloads.mysql.com/archives/community/1. 获取mysql安装包&#xff0c;mysql-5.7…...

现在有男的做外围女网站客服吗/培训机构加盟

参考1&#xff1a;http://mofeichen.iteye.com/blog/749734 格式化模板导入步骤 1.点击Window->Preference->Java -> Code Style -> Formatter 2.点击右侧Import选择*.xml模板文件导入即可 3.如果需要对模板进行修改&#xff0c;可点击Edit编辑即可 注释模板导…...

网页设计程序代码/seo推广学院

题意 给一个序列&#xff0c;支持两个操作&#xff1a;将一段区间中的每一个\(a_i\)赋值为\(c^{a_i}\)&#xff0c;\(c\) 给定&#xff1b;区间求和&#xff0c;对\(mod\)取模&#xff0c;不保证\(mod\)为质数 思路 显然线段树&#xff0c;然而此题先要单点修改 计算中指数会非…...

酒泉住房和城乡建设委员会网站/疫情最新消息今天公布

当前&#xff0c;政务信息孤岛现象已经成为制约简政放权、放管结合、优化服务改革的重要因素。所谓政务信息孤岛&#xff0c;是指政务信息数据被分割存储在不同部门的信息系统中&#xff0c;无法实现互联互通、互相分享、整合利用&#xff0c;难以为居民、企业和社会组织等提供…...