首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Spring 动态代理时是如何解决循环依赖的?为什么要使用三级缓存?

Spring 动态代理时是如何解决循环依赖的?为什么要使用三级缓存?

作者头像
程序员小航
发布于 2021-02-23 08:06:41
发布于 2021-02-23 08:06:41
2.1K00
代码可运行
举报
文章被收录于专栏:程序员小航程序员小航
运行总次数:0
代码可运行

前言

在研究 『 Spring 是如何解决循环依赖的 』 的时候,了解到 Spring 是借助三级缓存来解决循环依赖的。

同样在上一节留下了疑问:

  1. 循环依赖为什么要使用三级缓存?而不是使用二级缓存?
  2. AOP 动态代理对循环依赖的有没有什么影响?

本篇文章也是围绕上面的内容进行展开。

笔记也在不断整理,之前可能会有点杂乱。

循序渐进,看一看什么是循环依赖?

开始先简单回顾一下 Bean 的创建过程,当然小伙伴也可以直接阅读『 单例 Bean 的创建 』这篇文章。

不过考虑到阅读本文前再阅读上一篇文章、Debug 等等,会比较耗时,所以本篇文章前面一小部分会先对之前的文章内容做简要概括,也相当于对我自己学习的知识进行一个总结。

先来回顾一下三级缓存的概念。

singletonObjects: 一级缓存,存储单例对象,Bean 已经实例化,初始化完成。 earlySingletonObjects: 二级缓存,存储 singletonObject,这个 Bean 实例化了,还没有初始化。 singletonFactories: 三级缓存,存储 singletonFactory。

Bean 的创建过程
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class CircularServiceA {
    private String fieldA = "字段 A";
}

单例 Bean 的创建过程

通过上面的流程,可以看出 Spring 在创建 Bean 的过程中重点是在 AbstractAutowireCapableBeanFactory 中的以下三个步骤:

  1. 实例化 createBeanInstance: 其中实例化 Bean 并对 Bean 进行赋值,像例子中的 fieldA 字段在这里就会赋值。
  2. 属性注入 populateBean: 可以理解为对 Bean 里面的属性进行赋值。(会依赖其他 Bean)
  3. 初始化 initializeBean: 执行初始化和 Bean 的后置处理器。

实例化赋值源码可以阅读: BeanUtils.instantiateClass(constructorToUse)

如果要依赖其他 Bean 呢?

那如果 CircularServiceA 依赖了其他 Bean 呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class CircularServiceA {

    private String fieldA = "字段 A";

    @Autowired
    private CircularServiceB circularServiceB;

}
@Service
public class CircularServiceB {

}

A 依赖了 B

当 A 依赖了 B 的时候,在 createBeanInstance 这一步,并不会对 B 进行属性赋值。

而是在 populatedBean 这里查找依赖项,并创建 B。

循环依赖下的创建过程

循环依赖的场景,在上一篇文章已经有所讲解,这里仅仅画图说明一下。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class CircularServiceA {

    private String fieldA = "字段 A";

    @Autowired
    private CircularServiceB circularServiceB;

}
@Service
public class CircularServiceB {
    @Autowired
    private CircularServiceA circularServiceA;
}

A B 循环依赖

在 A 和 B 循环依赖的场景中:

B populatedBean 查找依赖项 A 的时候,从一级缓存中虽然未获取到 A,但是发现 A 在创建中。

此时,从三级缓存中获取 A 的 singletonFactory 调用工厂方法,创建 getEarlyBeanReference A 的早期引用并返回。

B 引用到 A ,B 就可以初始化完毕,然后 A 同样也可以初始化完毕了。

二级缓存能否解决循环依赖

通过上面的图,仔细分析一下,其实把二级缓存拿掉,在 B 尝试获取 A 的时候直接返回 A 的实例,是不是也是可以的?

答案是:可以的!

但是为什么还是用三级缓存呢?

网上的很多资料说是和动态代理有关系,那就从动态代理的方面继续往下分析分析。

动态代理的场景

在 JavaConfig(配置类) 上添加 @EnableAspectJAutoProxy 注解,开启 AOP ,通过 Debug 循序渐进看一看动态代理对循环依赖的影响。

动态代理下,Bean 的创建过程
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class CircularServiceA {
    private String fieldA = "字段 A";

    public void methodA() {

        System.out.println("方法 A 执行");

    }
}
@Aspect
@Component
public class AspectA {

    @Before("execution(public void com.liuzhihang.circular.CircularServiceA.methodA())")
    public void beforeA() {
        System.out.println("beforeA 执行");
    }
}

只有 A 的情况下,给 A 添加切面,开始 Debug。

前面的流程都相同,在 initializeBean 开始出现差异。

这一步需要初始化 Bean 并执行 Bean 的后置处理器。

执行后置处理器

其中有一个处理器为:AnnotationAwareAspectJAutoProxyCreator 其实就是加的注解切面,会跳转到 AbstractAutoProxyCreator 类的 postProcessAfterInitialization 方法

postProcessAfterInitialization

如图所示:wrapIfNecessary 方法会判断是否满足代理条件,是的话返回一个代理对象,否则返回当前 Bean。

后续调用 getProxy 、createAopProxy 等等,最终执行到下面一部分。

最终会执行到这里,AOP 代理相关的就不细看了。

一路放行,直到 initializeBean 执行结束。

A 被替换为了代理对象

此时发现:A 被替换为了代理对象。

所以 doCreateBean 返回,以及后面放到一级缓存中的都是代理对象。

红框部分为差异

有循环依赖的动态代理

这一次把循环依赖打开:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class CircularServiceA {

    private String fieldA = "字段 A";

    @Autowired
    private CircularServiceB circularServiceB;

    public void methodA() {

        System.out.println("方法 A 执行");

    }
}
@Aspect
@Component
public class AspectA {

    @Before("execution(public void com.liuzhihang.circular.CircularServiceA.methodA())")
    public void beforeA() {

        System.out.println("beforeA 执行");

    }

}
@Service
public class CircularServiceB {

    @Autowired
    private CircularServiceA circularServiceA;

    public void methodB() {

    }
}
@Aspect
@Component
public class AspectB {
    
    @Before("execution(public void com.liuzhihang.circular.CircularServiceB.methodB())")
    public void beforeB() {

        System.out.println("beforeB 执行");

    }

}

开始 Debug,前面的一些列流程,都和正常的没有什么区别。而唯一的区别在于,创建 B 的时候,需要从三级缓存获取 A。

此时在 getSingleton 方法中会调用:singletonObject = singletonFactory.getObject();

B 属性赋值时,从三级缓存获取 A

有时会比较疑惑 singletonFactory.getObject() 调用的是哪里?

三级缓存获取对象

所以这一块调用的是 getEarlyBeanReference,开始遍历执行 BeanPostProcessor

getEarlyBeanReference

getEarlyBeanReference

看到 wrapIfNecessary 就明白了吧!这块会获取一个代理对象

也就是说此时返回,并放到二级缓存的是一个 A 的代理对象。

这样 B 就创建完毕了!

到 A 开始初始化并执行后置处理器了!因为 A 也有代理,所以 A 也会执行到 postProcessAfterInitialization 这一部分!

判断二级缓存

但是在执行 wrapIfNecessary 之前,会先判断代理对象的标记缓存是否有 A 了。

this.earlyProxyReferences.remove(cacheKey) != bean

但是这块获取到的是 A 的代理对象。肯定是 false 。所以不会再生成一次 A 的代理对象。

代理 - 循环依赖

总结

可以看到,循环依赖下,有没有代理情况下的区别就在:

singletonObject = singletonFactory.getObject();

在循环依赖发生的情况下 B 中的 A 赋值时:

  1. 无代理:getObject 直接返回原来的 Bean
  2. 有代理:getObject 返回的是代理对象

然后都放到二级缓存

为什么要三级缓存?
  1. 假设去掉三级缓存

去掉三级缓存之后,Bean 直接创建 earlySingletonObjects, 看着好像也可以。

如果有代理的时候,在 earlySingletonObjects 直接放代理对象就行了。

但是会导致一个问题:在实例化阶段就得执行后置处理器,判断有 AnnotationAwareAspectJAutoProxyCreator 并创建代理对象

这么一想,是不是会对 Bean 的生命周期有影响。

同样,先创建 singletonFactory 的好处就是:在真正需要实例化的时候,再使用 singletonFactory.getObject() 获取 Bean 或者 Bean 的代理。相当于是延迟实例化。

  1. 假设去掉二级缓存

如果去掉了二级缓存,则需要直接在 singletonFactory.getObject() 阶段初始化完毕,并放到一级缓存中。

B 和 C 都依赖 A

那有这么一种场景,B 和 C 都依赖了 A。

要知道在有代理的情况下 singletonFactory.getObject() 获取的是代理对象。

多次获取代理对象不同

而多次调用 singletonFactory.getObject() 返回的代理对象是不同的,就会导致 B 和 C 依赖了不同的 A。

那如果获取 B 到之后直接放到一级缓存,然后 C 再获取呢?

一级缓存放的是已经初始化完毕的 Bean,要知道 A 依赖了 B 和 C ,A 这时候还没有初始化完毕。

小结

循环依赖的场景有很多,本文只是通过 Debug ,来了解到循环依赖和 AOP 之间的关系,以及了解到为什么要用三级缓存。

当然,Spring 设计之初是什么样子的?如何一步一步发展成现在这种的?

肯定是不能慢慢去研究了,所以只能以现在的版本,去揣测作者的意图。

不足之处,多多指正。

- <End /> -

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员小航 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
图像生成 FLUX.1+ComfyUI模型部署,一起来生成独有的视觉盛宴
FLUX.1是由黑森林实验室(Black Forest Labs)研发的一款前沿图像生成模型,提供了三个不同版本,以满足多样化的用户需求。
忆愿
2024/10/29
8070
图像生成 FLUX.1+ComfyUI模型部署,一起来生成独有的视觉盛宴
丹摩|FLUX.1+ComfyUI部署与使用
随着人工智能(AI)技术的飞速发展,全球范围内的技术创新和产业变革正在加速。丹摩智算平台(DAMODEL)凭借其卓越的AI算力服务,迅速成为AI开发者的重要工具平台。该平台通过提供高效的GPU资源租赁服务,使得机器学习(ML)和深度学习(DL)项目的开发不再受限于本地硬件资源的瓶颈。开发者可以更加专注于算法创新与应用开发,而无需担忧计算资源的配备与维护问题。本文将详细介绍如何在丹摩智算平台上部署和使用FLUX.1及ComfyUI,帮助开发者快速搭建AI项目环境,提升开发效率。
用户11397231
2024/12/10
7391
丹摩|FLUX.1+ComfyUI部署与使用
DAMODEL丹摩|部署FLUX.1+ComfyUI实战教程
FLUX.1是由黑森林实验室(Black Forest Labs)开发的开源AI图像生成模型。它拥有12B(120亿)参数,是迄今为止最大的文生图模型之一。FLUX.1以其卓越的图像质量、高度逼真的人体解剖学表现和先进的提示词遵循能力而脱颖而出,为AI图像生成设定了新的行业标准。
fhvyxyci
2024/12/24
7690
DAMODEL丹摩|部署FLUX.1+ComfyUI实战教程
丹摩智算:FLUX.1+ComfyUI的部署与使用指南
在人工智能领域,图像生成技术一直是研究的热点之一。随着深度学习技术的发展,图像生成技术已经取得了显著的进步。最近,黑森林实验室(Black Forest Labs)开发的FLUX.1模型在图像合成技术方面取得了新的突破,成为了市场上的新宠。本文将介绍如何在丹摩智算平台上部署和使用FLUX.1模型,以及如何结合ComfyUI为用户提供更直观的交互体验。
屿小夏
2024/09/26
1.3K0
丹摩智算:FLUX.1+ComfyUI的部署与使用指南
丹摩征文活动|FLUX.1+ComfyUI高效部署策略与实践
前言:从最初的命令行界面,到图形用户界面(GUI),再到如今的智能化、个性化交互体验,每一次技术的革新都为我们带来了前所未有的便捷与惊喜。而在这个充满无限可能的舞台上,丹摩平台凭借其卓越的创新能力和深厚的技术积累,为我们呈现了一个全新的交互体验——FLUX.1+ComfyUI。
Eternity._
2024/11/12
2860
丹摩征文活动|FLUX.1+ComfyUI高效部署策略与实践
【零绘画基础程序员必学】AI绘画入门笔记03—初识ComfyUI+FlUX
前面介绍的SD的操作界面其实叫做 WebUI,这是SD的入门操作界面,它非常适合初学者快速上手。它的界面简洁,功能展示清晰,很适合用户理解SD的生图方式。
CS逍遥剑仙
2025/04/18
6170
CloudStudio之ComfyUI上使用Wan2.1输出高质量视频
启动后默认监听8188端口,直接在空间地址例如https://xxx.ap-singapore.cloudstudio.work/修改成https://xxx--8188.ap-singapore.cloudstudio.work/即可打开项目网站。
CloudStudio
2025/03/13
1.4K4
🔥 智谱清影 🔥-CogVideoX-2b-部署与使用,带你揭秘生成6s视频的极致体验!
过去的技术常常在效率和质量之间挣扎,而现在,它的 3D 变分自编码器能将视频数据压缩至原来的 2%,大幅降低资源消耗,并保持视频帧的连贯性。
忆愿
2024/10/23
3310
🔥 智谱清影 🔥-CogVideoX-2b-部署与使用,带你揭秘生成6s视频的极致体验!
炸裂!新版 SD WebUI Forge 出图速度更快!支持最新Flux 模型!(保姆级安装教程)
大家是不是经常为SD WebUI卡顿、爆显存而苦恼?一启动SD 电脑就开始发烫, 尤其低显存用户屡屡"中招",不得不一遍遍重启。作为AI绘画的必备工具,WebUI却还有这么多"坑",着实让人不爽!😠
贾维斯Echo
2024/09/11
2K1
炸裂!新版 SD WebUI Forge 出图速度更快!支持最新Flux 模型!(保姆级安装教程)
CloudStudio之Flux LoRA训练速通手册
运行中观察Volatile GPU-Util的值,需要大于0,一般是99%或100%。 如果是0,说明停止训练了。
CloudStudio
2025/03/13
3730
智谱AI:CogVideoX-2b——视频生成模型的得力工具
智谱AI公布了一项激动人心的技术创新:他们决定将他们开发的视频生成模型CogVideoX的源代码对外公开。
Skrrapper
2024/09/24
2890
智谱AI:CogVideoX-2b——视频生成模型的得力工具
DAMODEL丹摩|CogVideoX-2b:从安装到上线,轻松搞定全过程!
CogVideoX的推出标志着视频生成技术的一次重大突破。过去,如何在保持高效的同时提升视频质量一直是一个难题,但CogVideoX 通过其先进的3D变分自编码器,成功将视频数据压缩至原来的2%,大幅降低了资源消耗,并确保视频帧之间的流畅连贯。
用户11292525
2024/11/21
2940
DAMODEL丹摩|CogVideoX-2b:从安装到上线,轻松搞定全过程!
我用AI 3步生成了微信红包封面,全网都在求教程!
今年AI视频生成技术非常火爆,于是我便有了一个想法:能不能直接用AI生成一个好看的微信红包封面呢?
算法一只狗
2025/05/12
1510
老电脑焕发第二春,玩转 Stable Diffusion 3
几年前,我头脑一热,配置了一台顶配级消费 PC(RTX 2080 Ti GPU + i9 CPU),打算用来学习 AI。然而,起初我并没有找到合适的切入点。深度学习早期阶段,消费级显卡根本无法承担训练大模型、微调大模型,甚至连运行大模型都很吃力。结果,这台电脑主要用来学习 TensorFlow、Python 编程等基础知识,但最后从入门到放弃。不过,当时配置的 CPU 和内存还不错,用来编译 Chromium 浏览器和 Android 系统也算是物尽其用,唯独显卡几乎闲置。随着 Nvidia 不断推出新显卡,RTX 2080 Ti 显得越来越落伍了。
云水木石
2024/06/26
7570
老电脑焕发第二春,玩转 Stable Diffusion 3
丹摩平台 | 智谱清影 -CogVideoX-2b-部署与使用,带你揭秘生成6s视频的极致体验!
CogVideoX 的问世可以说是视频生成领域的一次革命性变革。传统技术在追求高效与高质的过程中常常面临两难,而如今,通过其先进的3D变分自编码器,视频数据压缩到了原本的2%,显著减少了资源使用,同时确保了视频帧之间的连贯流畅。
用户11316056
2024/11/21
2780
丹摩平台 | 智谱清影 -CogVideoX-2b-部署与使用,带你揭秘生成6s视频的极致体验!
FLUX.1 Stable diffusion开源120亿参数AI绘画史上最大的模型
在6月上旬,SD3刚刚开源了SD3的medium版本模型,当然SD3的最佳之处在于模型架构与Sora是同源架构,而不是参数量,因为SDXL就有101亿参数,当然了,SD3有3个版本,但是最大也是80亿,虽然不一定是越大越好,但是一个AI文生图大模型,都超越了一些开源的大语言模型,那就是我们的主角FLUX.1,一出手就获得了3000万美元的融资,而这仅仅是推出的三个版本中开源的一个。
疯狂的KK
2024/08/05
1.7K0
FLUX.1  Stable diffusion开源120亿参数AI绘画史上最大的模型
免配置,即刻体验FLUX强大图像编辑:ComfyUI-FLUX模板在Cloud Studio上线!
你是否对前沿的多模态AI图像编辑技术跃跃欲试,却被复杂的模型部署、环境配置、依赖安装所困扰?
CloudStudio
2025/07/04
3050
免配置,即刻体验FLUX强大图像编辑:ComfyUI-FLUX模板在Cloud Studio上线!
丹摩智算:探索CogVideoX-6B模型的部署与使用
智谱 AI 在 8 月 6 日宣布了一个令人兴奋的消息:他们将开源视频生成模型 CogVideoX,目前,其提示词上限为 226 个 token,视频长度为 6 秒,帧率为 8 帧 / 秒,视频分辨率为 720*480,而这仅仅是初代,性能更强参数量更大的模型正在路上。
屿小夏
2024/09/26
1870
丹摩智算:探索CogVideoX-6B模型的部署与使用
AI图像生成利器:Stable Diffusion 3.5本地运行与远程出图操作流程
本篇文章将介绍如何在Windows系统电脑本地部署Stable Diffusion 3.5,并利用cpolar内网穿透工具实现公网环境下的远程访问,制作高质量的AI图片。无需复杂的公网IP配置或重复部署,让你的创作过程更加便捷高效。
YY的秘密代码小屋
2024/12/22
1.7K0
AI图像生成利器:Stable Diffusion 3.5本地运行与远程出图操作流程
0成本!基于腾讯云Cloud Studio,打造属于自己的数字人口播系统,照片+音乐=自动开口,效果炸裂
原文链接:https://mp.weixin.qq.com/s/G39ZHVITRilKlXnKn3mkrA
CloudStudio
2025/03/17
6960
推荐阅读
图像生成 FLUX.1+ComfyUI模型部署,一起来生成独有的视觉盛宴
8070
丹摩|FLUX.1+ComfyUI部署与使用
7391
DAMODEL丹摩|部署FLUX.1+ComfyUI实战教程
7690
丹摩智算:FLUX.1+ComfyUI的部署与使用指南
1.3K0
丹摩征文活动|FLUX.1+ComfyUI高效部署策略与实践
2860
【零绘画基础程序员必学】AI绘画入门笔记03—初识ComfyUI+FlUX
6170
CloudStudio之ComfyUI上使用Wan2.1输出高质量视频
1.4K4
🔥 智谱清影 🔥-CogVideoX-2b-部署与使用,带你揭秘生成6s视频的极致体验!
3310
炸裂!新版 SD WebUI Forge 出图速度更快!支持最新Flux 模型!(保姆级安装教程)
2K1
CloudStudio之Flux LoRA训练速通手册
3730
智谱AI:CogVideoX-2b——视频生成模型的得力工具
2890
DAMODEL丹摩|CogVideoX-2b:从安装到上线,轻松搞定全过程!
2940
我用AI 3步生成了微信红包封面,全网都在求教程!
1510
老电脑焕发第二春,玩转 Stable Diffusion 3
7570
丹摩平台 | 智谱清影 -CogVideoX-2b-部署与使用,带你揭秘生成6s视频的极致体验!
2780
FLUX.1 Stable diffusion开源120亿参数AI绘画史上最大的模型
1.7K0
免配置,即刻体验FLUX强大图像编辑:ComfyUI-FLUX模板在Cloud Studio上线!
3050
丹摩智算:探索CogVideoX-6B模型的部署与使用
1870
AI图像生成利器:Stable Diffusion 3.5本地运行与远程出图操作流程
1.7K0
0成本!基于腾讯云Cloud Studio,打造属于自己的数字人口播系统,照片+音乐=自动开口,效果炸裂
6960
相关推荐
图像生成 FLUX.1+ComfyUI模型部署,一起来生成独有的视觉盛宴
更多 >
交个朋友
加入腾讯云官网粉丝站
蹲全网底价单品 享第一手活动信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档