Spring解决循环依赖为什么需要三级缓存?
前言
什么是循环依赖呢?我们抛开Spring这个框架来聊下什么是循环依赖,循环依赖可能在我们平时的开发过程中是属于比较常见的。Spring容器最大的功能就是对bean的生命周期进行管理,每个bean在创建的过程中,需要得到一个完整的bean需要对bean的所有属性进行赋值,如果两个bean出现了相互依赖的情况,如果Spring没有处理循环依赖,那么出现的结果就是在bean的创建过程中出现相互依赖,导致这个bean永远无法创建出来,则就导致一直在相互创建,那么Spring是如何来解决循环依赖的呢?
什么情况下会循环依赖
1.先看如下demo: B和A相互循环依赖
@Component
public class B {@Autowiredprivate A a;
}@Component
public class A {@Autowiredprivate B b;
}启动项目:结果没有报错。
2.加入异步逻辑修改
@Component
public class A {@Autowiredprivate B b;@Asyncpublic void test(){}
}@Component
public class B {@Autowiredprivate A a;
}
@EnableAsync
public class App {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(App.class);}}启动后 :

解决方案:加入lazy注解
@Component
public class B {@Autowired@Lazyprivate A a;
}
@Component
public class A {@Autowiredprivate B b;@Asyncpublic void test(){}
}启动后:没有异常
上面发现使用@Async异步注解,循环依赖就会报错,有可能是因为有了@Async注解修饰的方法,其对应的类被代理了,那代理了就会报错么?我们继续尝试事务注解看看。
@Component
public class A {@Autowiredprivate B b;@Transactionalpublic void test(){}
}@Component
public class B {@Autowiredprivate A a;
}
@EnableTransactionManagement
public class App {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(App.class);}}启动后:正常,没有报错。
于是我们不经要问:
循环依赖本来不会报错,为何添加@Async异步注解后就会导致报错
为何添加@Transactional注解就不会报错
使用了@Async异步注解的循环依赖,为何可以使用@lazy注解解决
我们要想清楚上面的问题,就需要了解Bean的生命周期。
Bean的生命周期
一个简单的Bean生命周期如下:

问题出现就属性赋值这里:

由图:我们知道,当B也依赖A时,需要去容器中找到A,A已经实例化了,只是还没属性赋值,所以,不应该再实例化,解决方案:在A创建的实例化后,用一个map存起来A来不就行了么?于是有了二级缓存

似乎上面已经可以解决循环依赖了,但细想一下我们会发现问题:

通过上面的逻辑,我们发现了问题所在,B赋值属性A时,如果从Map中直接获取,那么得到的是原生对象,如果后续A没有被代理,一切没问题,如果A被代理了,那么B得到的对象就不对了,怎么解决,如果我们将aop提前是不是解决了问题。


由于A对象的Aop方式提前了,那么B依赖的A就是代理对象了,A对象执行赋值后,后续到Aop这一步,会判断是否已经AOP过了,是的话就不会再Aop了,问题来了:如果C也跟A相互依赖,难道C去依赖A时,也要通过ObjectFactory获取A的代理对象么?如果是这样,A就存在2个代理对象了,A是单例的,因此这样不行,于是产生了一个新的缓存,我们称之为三级缓存。

于是,spring似乎完美解决了循环依赖问题?但为何使用@Async进行异步代理,会报错?

我们看看报错的原因就知道:

那为何@Transactional修饰就没问题呢?
原因是因为:ObjectFactory.getObject()方法可以产生代理对象
为何使用@lazy注解修饰就能解决问题呢?

我们看看源码:





从源码来看,为何@Aync注解修饰,不能在ObjectFactory.getObject()方法实现代理对象:



而@Tranctional注解相关的处理器



那么问题?如果A已经在getObject()方法后产生了代理类,后续init()方法后,还会执行代理么?答案是不会了,因为:


总结
1、三级缓存各自的作用
第一级缓存存的是对外暴露的对象,也就是我们应用需要用到的
第二级缓存的作用是为了处理循环依赖的对象创建问题,里面存的是半成品对象或半成品对象的代理对象
第三级缓存的作用处理存在 AOP + 循环依赖的对象创建问题,能将代理对象提前创建
2、Spring 为什么要引入第三级缓存
严格来讲,第三级缓存并非缺它不可,因为可以提前创建代理对象
提前创建代理对象只是会节省那么一丢丢内存空间,并不会带来性能上的提升,但是会破环 Spring 的设计原则
Spring 的设计原则是尽可能保证普通对象创建完成之后,再生成其 AOP 代理(尽可能延迟代理对象的生成)
所以 Spring 用了第三级缓存,既维持了设计原则,又处理了循环依赖;牺牲那么一丢丢内存空间是愿意接受的
相关文章:
Spring解决循环依赖为什么需要三级缓存?
前言什么是循环依赖呢?我们抛开Spring这个框架来聊下什么是循环依赖,循环依赖可能在我们平时的开发过程中是属于比较常见的。Spring容器最大的功能就是对bean的生命周期进行管理,每个bean在创建的过程中,需要得到一个完整的bean需…...
Android源码分析 - 回顾Activity启动流程
跟踪Activity启动流程 基于 Android8.0 源码跟踪 Android8/9大同小异,但Android10对activity的管理解耦交给了ATMS。 跟踪目的:ams到底在哪里发起activity的启动的?以及resume等生命周期到底是谁发起的?onResume()之后是哪里发起…...
PDMS二次开发(一)——PML类型程序类型与概念
目录前言一、PML类型与概念基础知识变量函数小例子注释PML表达式条件判断语句循环skip和break窗口程序在PDMS菜单栏中添加程序窗口自动定位PML常见控件前言 PDMS二次开发需要.net 有自带的PML语言和C# .net一般通常泛指的是C#语言 模型数据借助.NET的接口可以转换成数据库中的…...
一文揭晓:手机号码归属地api的作用是什么?
随着手机的普及,手机号码的归属地已经成为很多网站和App中调用的重要数据资源。而手机号码归属地API可以帮助开发者快速获取手机号码归属地信息。目前,这种API已经被广泛地使用,用于各种不同的应用场景。这对于用户及开发者来说是非常重要的&…...
电容的结构分类介质封装及应用场景总结
🏡《总目录》 目录 1,概述2,结构分类2.1,固定电容器2.2,可变电容器3,介质分类3.1,无机介质电容器3.2,有机介质电容器3.3,电解电容器3.4,气体介质电容器4,封装分类4.1,直插电容器4.2,贴片电容器5,总结1,概述 电容器作为一种储能元件,在电路中和电阻一样非常常用…...
数据结构初阶——时间复杂度与空间复杂度
时间复杂度与空间复杂度1. 算法效率1.1 如何衡量一个算法的好坏1.2算法的复杂度2.时间复杂度2.1 时间复杂度的概念2.2 大O的渐进表示法2.3常见时间复杂度计算举例实列1:实列2:实列3:实列4:实列5:实列6:实列…...
深度学习之“制作自定义数据”--torch.utils.data.DataLoader重写构造方法。
深度学习之“制作自定义数据”–torch.utils.data.DataLoader重写构造方法。 前言: 本文讲述重写torch.utils.data.DataLoader类的构造方法,对自定义图片制作类似MNIST数据集格式(image, label),用于自己的Pytorc…...
#G. 求约数个数之六
我们先求到区间[1..b]之间的所有约数之和于是结果就等于 [1..b]之间的所有约数之和减去[1..a-1]之间的约数之和很明显这两个问题是同性质的问题,只是右端点不同罢了.明显对于1到N之间的数字,其约数范围也为1到N这个范围内。于是我们可以枚举约数L,当然这…...
如何为Java文件代码签名及添加时间戳?
Java是一种流行的编程语言,大多数组织都使用它来开发业务应用程序。由于其高使用率,攻击者总是试图找到其中的漏洞并基于它利用软件。为了防止此类攻击, 为 Java 文件(.jar)进行代码签名并添加时间戳,可以防…...
Xamarin.Forsm for Android 显示 PDF
背景 某些情况下,需要让用户阅读下发的文件,特别是红头文件,这些文件一般都是使用PDF格式下发,这种文件有很重要的一点就是不能更改。这时候就需要使用原文件进行展示。 Xamarin.Forms Android 中的 WebView 控件是不能直接显示的…...
RK3399平台开发系列讲解(LED子系统篇)LED子系统详解
🚀返回专栏总目录 文章目录 一、设备树编写二、LED子系统2.1、用户态2.2、内核驱动三、驱动代码3.1、平台设备驱动的注册3.2、平台设备驱动的probe四、使用方法沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将详细介绍LED子系统。 一、设备树编写 节点属性添加…...
LeetCode 432. 全 O(1) 的数据结构
LeetCode 432. 全 O(1) 的数据结构 难度:hard\color{red}{hard}hard 题目描述 请你设计一个用于存储字符串计数的数据结构,并能够返回计数最小和最大的字符串。 实现 AllOneAllOneAllOne 类: AllOne()AllOne()AllOne() 初始化数据结构的对…...
再析jvm
前言 希望自己每一次学习都有不同的理解 文章目录前言1. jvm的组成取消永久代使用元空间原因2. 运行时数据区3. 堆栈区别队列和栈,队列先进先出,栈先进后出从栈顶弹出4. GC、内存溢出、垃圾回收4.1 如何确定引用是否会被回收4.1.1 Java中的引用类型4.1.…...
社招前端二面面试题总结
代码输出结果 var A {n: 4399}; var B function(){this.n 9999}; var C function(){var n 8888}; B.prototype A; C.prototype A; var b new B(); var c new C(); A.n console.log(b.n); console.log(c.n);输出结果:9999 4400 解析: conso…...
人人能读懂redux原理剖析
一、Redux是什么? 众所周知,Redux最早运用于React框架中,是一个全局状态管理器。Redux解决了在开发过程中数据无限层层传递而引发的一系列问题,因此我们有必要来了解一下Redux到底是如何实现的? 二、Redux的核心思想…...
uniCloud云开发----7、uniapp通过uni-swiper-dot实现轮播图
uniapp通过uni-swiper-dot实现轮播图前言效果图1、官网实现的效果2、需求中使用到的效果图官网提供的效果图源码1、html部分2、js部分3、css部分根据需求调整轮播图前言 uni-swiper-dot.文档 uni-swiper-dot 轮播图指示点 - DCloud 插件市场 本次展示根据需求制作的和官网用到…...
IM即时通讯构建企业协同生态链
在当今互联网信息飞速发展的时代,随着企业对协同办公要求的提高,协同办公的定义提升到了智能化办公的范畴。大多企业都非常重视构建连接用户、员工和合作伙伴的生态平台,利用即时通讯软件解决企业内部的工作沟通、信息传递和知识共享等问题。…...
Python实现构建gan模型, 输入一个矩阵和两个参数值,输出一个矩阵
构建一个GAN模型,使用Python实现,该模型将接受一个矩阵和两个参数值作为输入,并输出另一个矩阵。GAN(生成对抗网络)是一种深度学习模型,由生成器和判别器两部分组成,可以用于生成具有一定规律性的数据,如图像或音频。 # 定义生成器 def make_generator(noise_dim, dat…...
开学准备哪些电容笔?ipad触控笔推荐平价
在现代,数码产品的发展受到高技术的驱动。不管是在工作上,还是在学习上,大的显示屏可以使图像更加清晰。Ipad将成为我们日常生活中不可或缺的一部分,无论现在或将来。如果ipad配上一款方便操作的电容笔,将极大地提高我…...
放下和拿起 解放自己
放下太难,从过去中解放自己 工作这么久了,第一次不拿包上班,真爽 人的成长都是在碰撞和摸索中产生的,通过摸索,知道自己能力的边界和欲望的边界以及身体的边界,这三个决定了 你能做什么 你能享受什么&…...
从一次生产事故复盘:我们如何优雅地处理用户上传的‘异常’Excel文件(附Apache POI配置详解)
从生产事故到防御体系:构建Excel文件处理的工程化解决方案那天凌晨2点,我被一阵急促的告警声惊醒。监控系统显示,核心文件处理服务的错误率在10分钟内飙升到35%,大量用户上传的Excel文件无法正常解析。更糟糕的是,部分…...
Shiro RememberMe反序列化漏洞深度解析与实战利用
1. 这个漏洞不是“老古董”,而是理解Java安全边界的活教材很多人看到CVE-2016-4437,第一反应是“Shiro都淘汰了,还讲这个干啥?”——我去年在给一家做政企内部系统的客户做渗透复测时,就遇到过一个上线三年的审批平台&…...
新手也能懂的SSRF漏洞实战:用iwebsec靶场复现文件读取与内网探测
从零开始掌握SSRF漏洞:iwebsec靶场实战指南1. 认识SSRF漏洞的本质想象一下,你正在一家高档餐厅点餐,服务员承诺可以帮你从任何地方获取食材——包括隔壁竞争对手的厨房。SSRF(Server-Side Request Forgery)漏洞就像这个…...
别再手动点菜单了!用这招让Cadence Virtuoso Schematic效率翻倍(附Net高亮快捷键配置)
电路设计效率革命:Cadence Virtuoso Schematic高阶快捷键配置指南 在集成电路设计的浩瀚宇宙中,Cadence Virtuoso如同设计师手中的光刻机,每一次精准操作都直接影响最终芯片的性能与可靠性。然而,当面对数百个晶体管组成的复杂模…...
利用DiSEqC协议与AVR单片机驱动卫星天线电机改造户外设备
1. 项目概述:用卫星天线电机驱动一切如果你手头有一些需要承受风吹日晒、还得精确转动的设备,比如一个户外的大型定向天线,或者一个需要定期调整角度的太阳能板支架,甚至是一个坚固的监控云台,你可能会为驱动机构发愁。…...
DeepSeek基准测试避坑手册:92%开发者忽略的4大陷阱——硬件配置偏差、tokenizer不一致、batch size幻觉、温度值污染
更多请点击: https://codechina.net 第一章:DeepSeek基准测试避坑手册:92%开发者忽略的4大陷阱——硬件配置偏差、tokenizer不一致、batch size幻觉、温度值污染 硬件配置偏差:GPU显存与计算精度的隐性干扰 在A100(8…...
鸿蒙系统微博应用锁常见问题解答
为微博设置应用锁后,不少用户会有各种疑问:忘记密码怎么办?会不会影响消息推送?能不能只锁定某些功能?应用锁耗电吗?本文将针对这些高频问题逐一解答,帮助您更好地使用鸿蒙系统(Harm…...
警惕!AI正在悄悄重构全球攻防格局
警惕!AI 正在悄悄重构全球攻防格局 热点聚焦 AI重构网络安全:全球巨头加速布局 2026年5月,全球网络安全领域迎来重大变革,AI技术正在重塑攻防格局。OpenAI发布专为网络安全防御打造的集成化AI平台Daybreak,将安全防…...
放弃编码器!纯靠MPU6050和PID算法,手把手教你用TT马达实现平衡小车稳定控制(STM32F103C8T6实战)
纯MPU6050STM32F103的TT马达平衡车实战:无编码器PID控制全解析当大多数平衡小车方案都在强调编码器对速度反馈的不可或缺性时,我们决定挑战一个更极简的配置:仅用5美元的TT马达、9轴的MPU6050和STM32F103C8T6最小系统板,完全舍弃编…...
搞定这 5 个全栈电商项目,面试别再用 Todo-List 凑数了
找独立开发练手项目或者写简历项目时,最忌讳两件事:一是太简单(纯前端 Mock 数据,点两下就没了),二是太假(一上来就硬套微服务、消息队列、高并发,结果自己根本Hold不住)…...
