Spring-循环依赖简述

什么是循环依赖

// A依赖了B
class A
{
    public B b;
}
​
// B依赖了A
class B
{
    public A a;
}
​
// 循环依赖
A a = new A();
B b = new B();
a.b = b;
b.a = a;

对象之间的相互依赖很正常,但是在Spring中由于对象创建要经过Bean的生命周期,所以就有了循环依赖问题

Bean的生命周期

在Spring中,被Spring管理的对象叫做Bean,那么Bean是如何生成的?

1、Spring扫描class得到BeanDefinition,放在一个Map里

2、根据得到的BeanDefinition去生成Bean

3、首先根据class推断构造方法

4、根据推断出来的构造方法,反射得到一个对象

5、填充原始对象中的属性,依赖注入

6、如果原始对象中的某个方法被AOP了,那么则需要根据原始对象生成一个代理对象

7、把最终生成的代理对象放入单例池中,下次getBean直接从单例池拿即可

这里只列举出大致流程,还有Aware回调、初始化等

出现循环依赖的流程简述:

ABean创建-->依赖了B属性-->触发BBean创建--->B依赖了A属性--->需要ABean(但ABean还在 创建过程中)

在Spring中,就是通过三级缓存机制解决的循环依赖问题

三级缓存

一级缓存为:singletonObjects;二级缓存为:earlySingletonObjects;三级缓存为:singletonFactories

三个缓存的作用:

  • singletonObjects中缓存的是已经经历了完整生命周期的bean对象。

  • earlySingletonObjects比singletonObjects多了一个early,表示缓存的是早期的bean对象。也就是Bean的生命周期还没走完就把这个Bean放入了earlySingletonObjects。

  • singletonFactories中缓存的是ObjectFactory,表示对象工厂,表示用来创建早期bean对象的工厂。

循环依赖问题:A创建时--->需要B---->B去创建--->需要A,从而产生了循环

打破循环依赖,可以加个中间人(缓存)

一个缓存就能解决循环依赖了,那么为什么Spring中还需要singletonFactories呢?

如果A的原始对象注入给B的属性之后,A的原始对象进行了AOP产生了一个代理对象,此时对于A而言,它的Bean对象其实应该是AOP之后的代理对象,而B的a属性对应的并不是AOP之后的代理对象,这就产生了冲突。B依赖的A和最终的A不是同一个对象

现在Spring所用的singletonFactories,为了调和不同的情况,在singletonFactories中存的是lambda表达式,这样的话,只有在出现了循环依赖的情况,才会执行lambda表达式,才会进行AOP,也就说只有在出现了循环依赖的情况下才会打破Bean生命周期的设计,如果一个Bean没有出现循环依赖,那么它还是遵守了Bean的生命周期的设计的。

singletonFactories中存的是某个beanName对应的ObjectFactory,在bean的生命周期中,生成完原始对象之后,就会构造一个ObjectFactory存入singletonFactories中。这个ObjectFactory是一个函数式接口,所以支持Lambda表达式:() -> getEarlyBeanReference(beanName, mbd, bean)

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}
​
// InstantiationAwareBeanPostProcessorAdapter
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
    return bean;
}
​
// AbstractAutoProxyCreator(AnnotationAwareAspectJAutoProxyCreator的父类,进行AOP用到)
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    return wrapIfNecessary(bean, beanName, cacheKey);
}

总结

1、singletonObjects:缓存经过了完整生命周期的bean

2、earlySingletonObjects:缓存未经过完整生命周期的bean,如果某个bean出现了循环依赖,就会提前把这个暂时未经过完整生命周期的bean放入earlySingletonObjects中,这个bean如果要经过AOP,那么就会把代理对象放入earlySingletonObjects中,否则就是把原始对象放入earlySingletonObjects

3、singletonFactories:缓存的是一个ObjectFactory,也就是一个Lambda表达式。在每个Bean的生成过程中,经过实例化得到一个原始对象后,都会提前基于原始对象暴露一个Lambda表达式,并保存到三级缓存中,这个Lambda表达式可能用到,也可能用不到,如果当前Bean没有出现循环依赖,那么这个Lambda表达式没用,当前bean按照自己的生命周期正常执行,执行完后直接把当前bean放入singletonObjects中,如果当前bean在依赖注入时发现出现了循环依赖,则从三级缓存中拿到Lambda表达式,并执行Lambda表达式得到一个对象,并把得到的对象放入二级缓存。

4、还有一个缓存,就是earlyProxyReferences,用来记录某个原始对象是否进行过AOP了。在AbstractAutoProxyCreator的postProcessAfterInitialization方法中,会去判断当前beanName是否在earlyProxyReferences,如果在则表示已经提前进行过AOP了,无需再次进行AOP。

思考:为什么需要singletonFactories?假设没有singletonFactories,只有earlySingletonObjects会怎样?

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【广州华锐互动】气象卫星监测AR互动教学软件为气象学习带来更多乐趣

由VR制作公司广州华锐互动开发的气象卫星监测AR互动教学软件是一款结合了增强现实(AR)技术与气象监测技术的教育软件。它通过直观、互动的方式,帮助学生更好地理解和掌握气象监测的基本知识和技能。本文将从气象卫星监测AR互动教学软件的应用场景、优势分析、实际意…

c#如何把字符串中的指定字符删除

可以使用以下四种方法: 一、使用关键字:Replace public string Replace(char oldChar,char newChar); 在对象中寻找oldChar,如果寻找到,就用newChar将oldChar替换掉。 1、实例代码: 2、执行结果: 二、Rem…

【CesiumJS入门】(11)加载LAS点云数据

前言 最近有两次投递简历以及面试都被问到了是否有三维点云数据处理相关的经验。然而我的岗位都没有和点云相关的工作任务,所以还是得自己加把劲呀。 本篇将从数据获取到加载来简易地介绍一个LAS点云数据的加载。 加载数据 首先,你得有一份LAS格式的…

node插件MongoDB(三)—— 库mongoose 的使用和数据类型

前言 提示:使用mongoose 的前提是你安装了node和 MongoDB。 mongoose 官网文档:http://mongoosejs.net/docs/index.html 文章目录 前言一、安装二、基本使用1. 打开bin目录的mongod.exe文件2. 基本使用的代码(连接mongodb 服务)3.…

虚拟机复制后,无法ping通问题解决

虚拟机复制后,无法ping通问题解决 可能出现的现象 ssh工具连接不上虚拟机;虚拟机ping不通外网或者ping不通内网其它虚拟机; 原因 原虚拟机和新复制出来的虚拟机的ip地址重复;原虚拟机和新复制出来的虚拟机的MAC地址重复&#…

【wp】2023鹏城杯初赛 Web web1(反序列化漏洞)

考点&#xff1a; 常规的PHP反序列化漏洞双写绕过waf 签到题 源码&#xff1a; <?php show_source(__FILE__); error_reporting(0); class Hacker{private $exp;private $cmd;public function __toString(){call_user_func(system, "cat /flag");} }class A {p…

基于ssm的校园办公室报修管理系统

基于ssm的校园办公室报修管理系统 摘要 基于SSM的校园办公室报修管理系统是一个现代化的、高效的报修平台&#xff0c;它能够帮助校园内的教职工和学生更方便、更快捷地提交和处理报修请求。该系统基于Spring、SpringMVC和MyBatis&#xff08;简称SSM&#xff09;开发&#xff…

计算机组成原理之指令

引言 关于riscv操作数 32个寄存器 | X0~X31|快速定位数据。在riscv中&#xff0c;只对寄存器中的数据执行算术运算 2^61个存储字 | 只能被数据传输指令访问。riscv体系采用的是字节寻址。 一个寄存器是8bytes&#xff0c;64位&#xff08;double word&#xff09; 每次取的…

K8S用户管理体系介绍

1 K8S账户体系介绍 在k8s中&#xff0c;有两类用户&#xff0c;service account和user&#xff0c;我们可以通过创建role或clusterrole&#xff0c;再将账户和role或clusterrole进行绑定来给账号赋予权限&#xff0c;实现权限控制&#xff0c;两类账户的作用如下。 server ac…

webpack的简单使用

什么是webpack&#xff08;去官网看详细的API&#xff09; 本质上&#xff0c;webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时&#xff0c;它会在内部从一个或多个入口点构建一个 依赖图(dependency graph)&#xff0c;然后将你项…

谷歌提出 AGI 完整路线图:目前 ChatGPT 只处于 AGI 的第一阶段

本心、输入输出、结果 文章目录 谷歌提出 AGI 完整路线图:目前 ChatGPT 只处于 AGI 的第一阶段前言谷歌 DeepMind 发布 AGI 分级框架发展 AGI 必须遵循6个基本原则什么是AGI图灵测试详解六大原则AGI 的五大发展过程阶段原文参考弘扬爱国精神谷歌提出 AGI 完整路线图:目前 Cha…

SpringBoot整合定时任务遇到的多实例问题

唠嗑部分 是这样&#xff0c;前几日完善了定时任务的日志记录&#xff0c;今日切换了服务器&#xff0c;多部署了一个节点&#xff0c;使用nginx负载均衡&#xff0c;但是查看日志却发现了如下情况 那糟糕了&#xff0c;传说中的多实例问题出现了&#xff0c;今天我们就来聊聊…

浙大计算机学院2024届推免直博生名单

名单&#xff1a; 分析&#xff1a; 浙大计算机学院共录取推免直博生158人&#xff0c;其中计算机科学与技术专业73人&#xff0c;人工智能专业7人&#xff0c;软件工程专业21人&#xff0c;网络空间安全专业19人&#xff0c;电子信息专业31人&#xff0c;设计专业7人 欢迎关…

图文解析 Nacos 配置中心的实现

目录 一、什么是 Nacos 二、配置中心的架构 三、Nacos 使用示例 &#xff08;一&#xff09;官方代码示例 &#xff08;二&#xff09;Properties 解读 &#xff08;三&#xff09;配置项的层级设计 &#xff08;四&#xff09;获取配置 &#xff08;五&#xff09;注册…

Java快速排序算法、三路快排(Java算法和数据结构总结笔记)[7/20]

一、什么是快速排序算法 快速排序的基本思想是选择一个基准元素&#xff08;通常选择最后一个元素&#xff09;将数组分割为两部分&#xff0c;一部分小于基准元素&#xff0c;一部分大于基准元素。 然后递归地对两部分进行排序&#xff0c;直到整个数组有序。这个过程通过 par…

如何从存档服务器上完全删除PDM用户

当创建新用户时使用“PDM 登录”类型&#xff08;如下图&#xff09;&#xff0c;PDM用户名和密码会存储于存档服务器的注册表中。 存档服务器的注册表位置如下&#xff1a; HKEY_LOCAL_MACHINE\SOFTWARE\SolidWorks\Applications\PDMWorks Enterprise\ArchiveServer\ConisioU…

响应式艺术作品展示前端html网站模板源码

响应式艺术作品展示网站模板是一款适合各种艺术作品在线展示的响应式网站模板下载。提示&#xff1a;本模板调用到谷歌字体库&#xff0c;可能会出现页面打开比较缓慢。 转载自 https://www.qnziyw.cn/wysc/qdmb/23778.html

C/C++轻量级并发TCP服务器框架Zinx-游戏服务器开发003:架构搭建-需求分析及TCP通信方式的实现

文章目录 1 项目总体架构2 项目需求2.1 服务器职责2.2 消息的格式和定义 3 基于Tcp连接的通信方式3.1 通道层实现GameChannel类3.1.1 TcpChannel类3.1.2 Tcp工厂类3.1.3 创建主函数&#xff0c;添加Tcp的监听套接字3.1.4 代码测试 3.2 协议层与消息类3.2.1 消息的定义3.2.2 消息…

详解JS的四种异步解决方案:回调函数、Promise、Generator、async/await

同步&异步的概念 在讲这四种异步方案之前&#xff0c;我们先来明确一下同步和异步的概念&#xff1a; 所谓同步(synchronization)&#xff0c;简单来说&#xff0c;就是顺序执行&#xff0c;指的是同一时间只能做一件事情&#xff0c;只有目前正在执行的事情做完之后&am…

Redis实战 | 使用Redis 的有序集合(Sorted Set)实现排行榜功能,和Spring Boot集成

专栏集锦&#xff0c;大佬们可以收藏以备不时之需 Spring Cloud实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9270827.html Python 实战专栏&#xff1a;https://blog.csdn.net/superdangbo/category_9271194.html Logback 详解专栏&#xff1a;https:/…