【Spring专题】Spring之Bean的生命周期源码解析——阶段二(三)(属性填充之循环依赖底层原理解析)

news2024/11/15 17:29:59

目录

  • 前置知识
    • 循环依赖的产生
    • Spring里面的3个Map
  • 课程内容
    • 一、只有一级缓存的推理演进
      • 1.1 直接将实例化后生成的对象放入到单例池里面
      • 1.1 引入一个中间Map存实例化后的早期对象(疑似二级缓存)
      • 1.3 解决1.2需要被代理的问题(疑似二级缓存)
      • 1.4 为什么要三级缓存
  • 学习总结

前置知识

循环依赖的产生

说到循环依赖大家都不陌生,循环依赖的代码,就是如下:

@Component
public class CircularA {

    @Autowired
    CircularB b;
}

@Component
public class CircularB {

    @Autowired
    CircularA a;
}

但是大家有没有想过,循环依赖是如何产生的,然后又是怎么解决的呢?这里,我想给大家推演一下,就像咱是Spring作者一样,思考如何循环依赖。

Spring里面的3个Map

在这里,我还是想提前给大家先大概解释一下,在获取单例bean的时候,Spring源码出现的3个Map。分别如下:

  • Map<String, Object> singletonObjects:一级缓存。这个就是我们常说的单例池,这里存放的bean,是经历了完整Spring生命周期的,【走完了Spring所设计的生命周期】(这里的经历完整生命周期不是说非得要经历什么实例化前后、初始化前后。简单说,是:Spring认可的,成熟的Bean)
  • Map<String, Object> earlySingletonObjects:二级缓存。直接直译过来,这里存的是【早期单例Bean】。何为早期?就是相对前面的【成熟Bean】,【还没有走完生命周期】的Bean
  • Map<String, ObjectFactory<?>> singletonFactories:三级缓存。直译过来是【单例bean的工厂】。其实我还是喜欢用一个之前提到过的专有名词去解释:生产Bean的钩子方法缓存

课程内容

一、只有一级缓存的推理演进

我们先来看个图,在没有三级缓存之前,只有一个一级缓存的时候,如果A依赖了B,B依赖了A,那么就会造成下面的现象:
在这里插入图片描述
很显然,在我们刚开创建的过程中,单例池里面是不会有对象B,也不会有对象A的。毕竟它们才走到第二步【注入属性】,它是在最后一步才会把生成好的对象放入单例池中。所以,上图的情况,如果没有外部干预的话,在这两个bean之间就形成了一个闭环,无法解开了。这显然不是我们想要的结果,对吧。那这个问题该如何解决呢?

1.1 直接将实例化后生成的对象放入到单例池里面

这时候一个很正常的想法是,我提前放入到单例池里面不就行了吗,如下所示:
在这里插入图片描述
这样不就打破了吗?嘿嘿嘿
只能说有点道理,但不多。因为,在多线程环境下,可能会把【没初始化完】的bean暴露出去。这时候如果有人来访问单例池,直接拿到了这个BeanA,然后去调用里面的方法,在没有【属性注入】过的情况下,不就G了吗?是的,这就是并发安全问题!这里只能直接pass这个方案了

1.1 引入一个中间Map存实例化后的早期对象(疑似二级缓存)

一个很正常的思考,我新增一个Map,在实例化后即刻存起来不就得了呗。反正都已经实例化了,地址已经固定了,后面再怎么操作都是对这个地址上的对象操作,提前把这个对象暴露出去,完全不影响结果啊。
在这里插入图片描述
如上图所示,那我新增一个中间缓存Map来存储之前实例化后的对象,总可以吧?嗯,从流程图上来看,这个真的好像是最终答案了。
不过,如果这时候我问你【AOP在哪?】,阁下将如何应对呢?很显然啊,这里存放的是原始对象,那如果我需要的是被代理的对象呢?看吧,这样稍微一推敲,又出现问题了。那好,我们继续完善这个方案就是了

1.3 解决1.2需要被代理的问题(疑似二级缓存)

在这里插入图片描述
就这样,多加上一步AOP过程不就行了嘛,嘿嘿嘿。不过按照惯例,我已经【嘿嘿嘿】了,所以肯定得问一句:真的行吗?哈,真的行!确实没问题了。那为什么,还要三级缓存呢?

1.4 为什么要三级缓存

讲到这里,我就要开始装逼了。(我甚至怀疑Spring这么写也是在【装逼】,哈哈,开个玩笑)
在这里插入图片描述
其实这个网上挺多论调的,我也是总结了百家之长,再结合我课堂上老师说的,总结出了以下结论:(算是个人之见,大家参考一下)

  1. 生命周期被打破这个我认为是最重要的原因,但是也比较难被理解的一点。怎么理解呢?大家还记得我以开始怎么形容Spring的吗?Spring的核心是什么?大家知道AOP的实现是在一块吗?
    • 第一个问题:Spring是实现了AOP技术的IOC容器
    • 第二个问题:Spring的核心是IOC跟AOP,但是,所有的基础都来自于IOC
    • 第三个问题:AOP的实现,是在bean生命周期的【初始化后】阶段。因为,AOP技术目前的实现,也是基于Spring提供的众多拓展点里面的某些个而已。比如AOP的实现就使用了:BeanPostProcessor。这里透露出来的意思是什么呢?我认为,它的意思是:在Spring内部,都只是把AOP当作额外拓展而已。就好像是我们基于Spring的拓展点实现了Mybatis,实现了SpringMVC一样的道理。

PS:所以到了这里大家伙知道这个生命周期被打破如何理解了吗?如果我们在实例化后就做判断是否需要做AOP的话,等于,还没【属性注入】,还没做【初始化前】、【初始化】、【初始化后】等等生命周期呢,就要开始了。并且呀,在实现这个AOP的过程中,你还得调用类似如下的方法:

for(BeanPostProcessor bp : this.beanPostProcessorsCache) {
		bp.postProcessAfterInitialization(bean);
}

但是这个代码,其实在后面的【初始化后】也会被调用的。我猜有的朋友会这么说:那我循环遍历实现了AOP的那几个指定的,实现了AOP的BeanPostProcessor不就行了吗?嗯,说实在确实行。不过,如果我们站在Spring的角度来看:AOP不过也是我IOC的一个拓展内容而已。这么来看的话,这么实现就侵入有点大了,而且语义上也稍微变了。

  1. 循环依赖出现频率。我想我呢问大家,你在实际使用场景中,循环依赖出现的多吗?弟弟我写Java代码4年多,我印象中就几次而已。so,你看看上面的解决方案如何?它每一次实例化生成Bean之后都做了判断!是否有点多余呢?
  2. 代码风格!说这个就很抽象了,但是对于Spring这种优秀的源码来说又有点情有可原。这句话怎么理解呢?

其实2、3要结合起来,一起建立在【1】最后的挣扎上,即我还是要在【实例化】完成后就开始判断是否需要AOP。

学习总结

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙应用开发学习路线(OpenHarmony/HarmonyOS)

鸿蒙应用开发学习路线&#xff08;OpenHarmony/HarmonyOS&#xff09; HarmonyOS应用开发学习路线网站汇总社区汇总视频学习路线 OpenHarmony应用开发学习路线与资料网站汇总社区汇总学习路线 MarkDown工具推荐 HarmonyOS应用开发学习路线 作者&#xff1a;坚果 团队&#xff1…

gremlin安装使用 详细步骤

gremlin是一个图数据库查询工具&#xff0c;注意他只是一个工具类似于dbeaver&#xff0c;navicat&#xff0c;sqlyog&#xff0c;是专门来分析图数据库的一个工具。 下载 下载地址Apache Download Mirrors 省事的可以直接 wget https://www.apache.org/dyn/closer.lua/tin…

LC-平衡二叉树

LC-平衡二叉树 链接&#xff1a;https://leetcode.cn/problems/balanced-binary-tree/description/ 描述&#xff1a;给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a;一个二叉树每个节点 的左右两个子树的…

电容笔哪个厂家的产品比较好?开学值得买电容笔推荐

开学快要到来了&#xff0c;各位学生党又开始为开学而准备&#xff0c;而电容笔对于学生党来说是必备的数码产品。苹果的正版Pencil&#xff0c;由于价格很贵&#xff0c;仅仅一支的售价就要近千块钱&#xff0c;所以很多人都买不起。现在国内的平板电脑电容笔已经很完美了&…

FreeCAD傻瓜式教程之约束设定和构建实体、开孔、调整颜色等

本内容基于官方教程中的绘制简单的零件中的体会&#xff0c;在初次绘制的时候&#xff0c;总是无法完成&#xff0c;几经尝试才发现其关键点所在&#xff0c;以此文记录&#xff0c;用以被查资料&#xff0c;同时也希望能够帮到纯白新手快速熟悉该软件的绘图方法。 一、. 打开…

基于YOLOV8模型的西红柿目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOV8模型的西红柿目标检测系统可用于日常生活中检测与定位西红柿目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法训练数…

创建延时队列

创建延时队列 queue.file_delay_destroy x-dead-letter-exchange: exchange.file_delay_destroy x-message-ttl: 259200000 259200000为3天,1000为1秒创建普通队列 queue.file_destroy创建普通交换机 type选择fanout 交换机绑定普通队列 (图中已经绑定&#xff0c;红框为…

救生员可以戴耳机吗,救生员佩戴蓝牙耳机会影响工作吗?

对于救生员这样一种常驻在水边的职位&#xff0c;戴耳机可以说是比较常见的&#xff0c;佩戴的最主要原因就在于方便进行沟通以及接受指令&#xff0c;以此来确保海边以及海滩等场所的安全&#xff0c;而在这种场景下&#xff0c;对于耳机的考验也是蛮大的&#xff0c;毕竟会出…

1.jvm和java体系结构

jvm简介 JVM&#xff1a;跨语言的平台 Java是目前应用最为广泛的软件开发平台之一。随着Java以及Java社区的不断壮大Java 也早已不再是简简单单的一门计算机语言了&#xff0c;它更是一个平台、一种文化、一个社区。 ● 作为一个平台&#xff0c;Java虚拟机扮演着举足轻重的…

什么是KNN( K近邻算法)

什么是KNN( K近邻算法) 虽然名字中有NN&#xff0c;KNN并不是哪种神经网络&#xff0c;它全名K-Nearest-Neighbors&#xff1a;K近邻算法&#xff0c;是机器学习中常用的分类算法。 物以类聚&#xff0c;人以群分。KNN的基础思想很简单&#xff0c;要判断一个新数据的类别&…

Kubernetes入门 八、StatefulSet控制器

目录 概述创建StatefulSetPod 的管理策略扩容缩容 镜像更新滚动更新分区更新删除更新 删除 概述 Stateful 翻译为 有状态的 &#xff0c;也是pod控制器的一种。 StatefulSet 是为了解决有状态应用的问题&#xff0c;Deployment是为无状态应用设计的。 无状态应用&#xff1a;…

Python 驱动连接 OceanBase 数据库

安装 JayDeBeApi 驱动 pip3 install JayDeBeApi 待更新 Python 驱动连接 OceanBase 数据库_云数据库 OceanBase 版-阿里云帮助中心

《你当像鸟飞往你的山》 书目总结

《你当像鸟飞往你的山》 书目总结

二、7.用户进程

TSS 是 x86CPU 的特定结构&#xff0c;被用来定义“任务”&#xff0c;它是内置到处理器原生支持的多任务的一种形式。 通过 call 指令&#xff0b;TSS 选择子的形式进行任务切换&#xff0c;此过程大概分成 10 步&#xff0c;这还是直接用 TSS 选择子进行任务切换的步骤&…

卷积神经网络——下篇【深度学习】【PyTorch】

文章目录 5、卷积神经网络5.10、⭐批量归一化5.10.1、理论部分5.10.2、代码部分 5.11、⭐残差网络&#xff08;ResNet&#xff09;5.11.1、理论部分5.11.2、代码部分 话题闲谈 5、卷积神经网络 5.10、⭐批量归一化 5.10.1、理论部分 批量归一化可以解决深层网络中梯度消失和…

分享因缺少 xPortSysTickHandler()函数而导致程序一直卡死在函数portTASK_FUNCTION的案例分析

今天来分享一个在学习freertos过程中遇到的一个小问题。就是发现程序跑不起来&#xff0c;但是debug调试时候发现也没有到while循环中&#xff0c;于是通过排查发现是因为缺少相应的SysTick中断服务函数导致的。话不多说&#xff0c;我们开始讲~ 问题锁定&#xff1a; 首先这…

springboot跨域踩坑笔记

事情是这样的&#xff0c;我在进行前后端联调的时候&#xff0c;发送了跨域拦截 马上在spring项目中创建一个CorsConfig类 package com.example.demo.config;import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.an…

聚观早报 | 抢先体验阿维塔11座舱;本田和讴歌采用NACS充电标准

【聚观365】8月21日消息 抢先体验阿维塔11鸿蒙座舱 本田和讴歌采用特斯拉NACS充电标准 华为秋季新品发布会将于9月12日举行 iQOO Z8即将到来 三星Galaxy S24系列外观或更改 抢先体验阿维塔11鸿蒙座舱 当前&#xff0c;智能座舱成了各大巨头跑马圈地的重要领域。根据毕马威…

国标GB28181安防视频平台EasyGBS通过对应密钥上传到其他平台展示的详细步骤来啦!

国标GB28181协议视频平台EasyGBS是基于国标GB28181协议的视频云服务平台&#xff0c;支持多路设备同时接入&#xff0c;并对多平台、多终端分发出RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流。平台可提供视频监控直播、云端录像、云存储、检索回放、智能告警、语音对讲、平台级…

C# 使用递归方法实现汉诺塔步数计算

C# 使用递归方法实现汉诺塔步数计算 Part 1 什么是递归Part 2 汉诺塔Part 3 程序 Part 1 什么是递归 举一个例子&#xff1a;计算从 1 到 x 的总和 public int SumFrom1ToX(int x) {if(x 1){return 1;}else{int result x SumFrom1ToX_2(x - 1); // 调用自己return result…