SpringBean的生命周期和循环依赖

news2024/11/27 10:37:20

Spring循环依赖

前言
大制作来啦,spring源码篇,很早之前我就想写一系列spring源码篇了,正好最近总是下雨,不想出门,那就让我来带大家走进Spring源码世界吧。
阅读建议
spring源码读起来有点难度,需要多Debug和做笔记,大家千万不要在一个方法里陷进去,不要想着只运行一次就能理解透,一定要先走几遍完整的流程。

一、IOC和依赖注入

在循环依赖之前,我想先简单的讲一下IOC和依赖注入,因为循环依赖这个问题就是出现在依赖注入。
IOC:控制反转,就是将对象的创建权力交给了容器,不需要自己手动去new对象

1.1 举个例子

传统创建bean

假设D依赖C,C依赖B,B依赖A,如果你需要创建D对象,那么你要从new D到new A,然后把A设置到B,把B设置到C,把C设置到D,这时候你才能拿到完整的D对象,是不是也不复杂?如果A-D还有10几个对象要维护,那是不是就想删库跑路了?
容器创建bean
控制反转就是将创建bean交给容器,你只要和容器说需要D对象,就能直接获取到了,是不是很方便,你可以会问:那容器是怎样创建的bean并将A-D的对象关联起来的呢?这正是我们今天要讲的内容。

二、循环依赖

对象间的依赖可能会出现循环依赖,下面就跟着我来看看spring是怎样将对象关联起来的,又是怎样解决循环以来的。

2.1 什么是循环依赖

如图,循环依赖分为三种,总的来说就是依赖形成了一个闭环,而打破这个闭环的就是今天重点要讲的三级缓存。
在这里插入图片描述

三、三级缓存

3.1 名词解析

在阅读源码前,先看看是哪三级缓存,分别都有什么作用。

  • 一级singletonObjects 保存已经(实例化、注入、初始化)完成的bean
  • 二级earlySingletonObjects 保存已经(实例化)完成的bean
  • 三级singletonFactories 保存bean创建工厂,便于创建代理对象

3.2 创建bean流程图

在这里插入图片描述

  1. 实例化User对象,将User放入第三级缓存
  2. 填充User的属性,发现依赖了UserClass,开始创建UserClass
  3. 实例化UserClass对象,将UserClass放入第三级缓存
  4. 填充UserClass的属性,发现依赖了User, 从第三级缓存中拿到User,将User放入第二级缓存
  5. 初始化UserClass,将UserClass添加到第一级缓存,删除第二第三级缓存
  6. User开始初始化,将User添加到第一级缓存,删除第二第三级缓存

3.3 能不能删除第二级缓存

看代码和流程图发现打破循环是第三级缓存的功劳,根本没用到二级缓存,那能不能删除第二级缓存呢?
答案肯定是不能的,让我举个例子
在这里插入图片描述
如果A需要找B、C,B需要找A,C也需要找A

  • B 找到 A 时,直接通过三级缓存的工厂的代理对象,生成对象 A1。
  • C 找到 A 时,直接通过三级缓存的工厂的代理对象,生成对象 A2。

通过 A 的工厂的代理对象,生成了两个不同的对象 A1 和 A2 ,所以为了避免这种问题的出现,我们搞个二级缓存,把 A1 存下来,下次再获取时,直接从二级缓存获取,无需再生成新的代理对象。

所以“二级缓存”的目的是为了避免因为 AOP 创建多个对象,其中存储的是半成品的 AOP 的单例 bean。

如果没有 AOP 的话,我们其实只要 1、3 级缓存,就可以满足要求。

四、代码调试

这里我以第二种情况为例,总的流程还是比较简单的。

4.1 第一步:创建实例

通过createBeanInstance(beanName, mbd, args)方法创建实例
在这里插入图片描述
进入createBeanInstance内部,发现BeanUtils通过反射Constructor.newInstance(Object… args)创建的实例。

在这里插入图片描述

4.2 第二步: 将实例保存到第三级缓存

添加到第三级缓存的地方是addSingletonFactory
在这里插入图片描述
这是个Lambda表达式,我们进入getEarlyBeanReference方法内部,发现如果不符合这段判断就直接返回4.1反射出来的对象,符合判断就进入这段代码,那这段返回的是什么呢?就是代理的对象
在这里插入图片描述
我们接着往下看,进入 getEarlyBeanReference(exposedObject, beanName) 方法,发现Spring会创建一个代理对象,并返回。
在这里插入图片描述
进入addSingletonFactory方法内部可以看到spring把singletonFactory放入了singletonFactories内部

4.3 第三步:填充属性

循环依赖就出在填充属性的过程,如以下情况,A等待B创建,B等待A创建,这样就形成循环依赖了。
进入populateBean(beanName, mbd, instanceWrapper)方法
第一个属性是userClass,就是user依赖的类,这里会调用getBean方法
在这里插入图片描述

4.3.1 实例化UserClass的Bean,放入第三级缓存

因为UserClass也没创建,所以这里创建过程和4.1、4.2是一样的

4.3.2 填充UserClasss属性

是不是进入循环了?如果没有三级缓存的话,假如UserClass有一个User属性,那么又要去创建User,就形成一个死循环了,三级缓存就是帮我们打破这个循环的。
我们从userClass的populateBean方法深入进来,它会从beanFactory获取user
在这里插入图片描述

4.3.3 获取user填充到UserClasss

进入beanFactory.getBean(resolvedName)方法内部,可以获取到创建bean的工厂,从三级工厂获取到bean的时候会将对象设置到二级缓存里,并删除三级缓存。
在这里插入图片描述
当调用singletonFactory.getObject()方法的时候进入到了getEarlyBeanReference,这就是4.2说的如果需要创建代理对象就返回一个代理对象,否则返回最开始实例化的对象。
在这里插入图片描述

4.4 第四步:初始化Bean

这里引入一个重要的概念:Bean的生命周期,在4.3将属性填充完成后,开始了bean的初始化过程,Bean的生命周期我们放到第五点来讲。在这里插入图片描述

4.5 第五步:将userClass添加到一级缓存

将初始化完成的userClass添加到一级缓存,删除第二级缓存
在这里插入图片描述

4.6 第六步 将userClass设置到user中

4.7 第七步 开始初始化user

4.8 第八步:将user添加到一级缓存

将初始化完成的user添加到一级缓存,删除第二级缓存

五、Bean生命周期

5.1 流程图

在这里插入图片描述

5.2 文字概述

  1. 调用Bean构造方法或工厂方法实例化Bean,将bean添加到三级缓存singletonFactories里面。
  2. 利用依赖注入完成Bean中所有属性值的配置注入,如果出现了循环依赖问题,会从三级缓存中解决问题。
  3. 如果Bean实现了各种Aware 接口,则调用对应的set方法。
  4. postProcessBefore对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。
  5. 如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
  6. postProcessAfter,此时Bean已经可以被应用系统使用了。
  7. 如果是"singleton",则归spring管生命周期;如果是"prototype",则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期。
  8. 如果Bean实现了DisposableBean 接口,则调用 destory() 方法销毁Bean;如果配置destory-method,则调用该方法销毁Bean。

六、总结

再来回顾三级缓存的作用

  1. 一级缓存:单例池,bean都初始化完成了,拿来就能用了
  2. 二级缓存:为了防止AOP代理出现多个对象
  3. 三级缓存:为了打破循环,保存创建bean的工厂方法

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

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

相关文章

性价比最高的护眼灯,护眼台灯应该怎么挑选

随着技术的发展,灯光早已成为每家每户都需要的东西。但是灯光不好可能会对眼睛造成伤害是很多人没有注意到的。现在随着护眼灯产品越来越多,市场上台灯的选择越来越多样化,如何选择一个对眼睛无伤害、无辐射的台灯成为许多家长首先要考虑的问…

limereport报表使用

在这里我使用报表是以报表的形式显示数据库的信息。所以首先需要准备的资料有:limereport源码,还有数据库,我这里使用的是qsqlite数据库。 1、下载limereport报表源码 2、运行自带的案例:demo_r1 3、点击 “Run Report Designer”…

RedisDesktopManager连不上redis问题解决(小白版)

常见问题就是 redis.conf配置文件 a.将port 127.0.0.1这一行注释掉 b.protected-mode保护模式改为no 这个可以看到很多博主都说了,相信都搜到这里来了你们都弄了,我就不详细说了 防火墙开放端口 我说明我自己的问题以及解决方法 1、执行telnet 虚拟…

2023年8月第1~2周大模型荟萃

2023年8月第1~2周大模型荟萃 2023.8.14版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 1、黑客制造了一款基于 AI 的恶意工具 FraudGPT 早先,有黑客制作了一个“没有道德限制”的 WormGPT 聊天机器人,可以自动生成…

PDF文件限制编辑怎么取消?

PDF文件设置了限制编辑,想要取消PDF文件的限制编辑,很简单,打开PDF编辑器,点击工具栏中的文件,选择属性按钮,进入到熟悉感界面之后,点击安全,然后我们点击权限下拉框,选择…

ModaHub魔搭社区:Milvus Cloud向量数据库可以部分避免AI幻觉

向量数据库的技术优势使其更适合在AI场景下应用,能够为AI的开发、增强内容生成的准确性提供重要的技术支撑。进一步来讲,向量数据库也被看作是大语言模型的记忆与灵魂,对于解决大模型的“幻觉”问题至关重要。 由于大模型是基于已有数据训练而…

TB/TM-商品详情原数据(APP)

一、接口参数说明: item_get_app-获得TB/TMapp商品详情原数据,点击更多API调试,请移步注册API账号点击获取测试key和secret 公共参数 请求地址: https://api-gw.onebound.cn/taobao/item_get_app 名称类型必须描述keyString是调用key&…

java+springboot+mysql员工管理系统

项目介绍: 使用javaspringbootmysql开发的员工管理系统(人力资源管理系统),系统包含超级管理员、管理员、员工角色,功能如下: 超级管理员:管理员管理;部门管理;职位管理…

嵌入式面试笔试刷题(day10)

文章目录 前言一、数组和链表的区别二、什么是内存对齐三、IIC的时序四、static作用五、查看tty设备的方法六、查找指定文件命令七、三次握手和四次挥手1.三次握手2.四次挥手 八、半关闭状态九、字节流和数据报总结 前言 本篇文章继续讲解笔试和面试。 一、数组和链表的区别 …

C++ STL stack queue

目录 一.stack 介绍 二.stack 使用 三.stack 模拟实现 普通版本: 适配器版本: 四.queue的介绍 五. queue使用 六.queue模拟实现 七.deque介绍 1.容器适配器 2.deque的简单介绍 3.deque的缺陷 4.为什么选择deque作为stack和queue的底层默认容…

SpringBoot复习:(46)全局的bean懒加载是怎么实现的?

在application.properties中配置: spring.main.lazy-initializationtrue在运行SpringApplication的run方法时,代码如下: 其中调用了prepareContext,prepareContext代码如下: 当在配置文件中配置了spring.main.lazy-initializat…

中国1990-2021连续30年土地利用数据CLCD介绍及下载

CLCD数据介绍 CLCD(China Land Cover Dataset)数据集由武汉大学黄昕老师公布,黄昕老师基于Google Earth Engine上335,709景Landsat数据,制作中国年度土地覆盖数据集(annual China Land Cover Dataset, CLCD),包含1985+1990—2020中国逐年土地覆盖信息。 为此,黄昕老师…

学好Elasticsearch系列-索引的批量操作

本文已收录至 Github,推荐阅读 👉 Java 随想录 先看后赞,养成习惯。 点赞收藏,人生辉煌。 文章目录 基于 mget 的批量查询基于 bulk 的批量增删改增加删除修改 filter_path Elasticsearch 提供了 _bulk API 来执行批量操作&#x…

Web APIs 第六天

正则表达式介绍语法元字符修饰符 一.正则表达式介绍 ① 简介 用来匹配字符串中字符组合的模式在JavaScript中,正则表达式也是对象通常用来查找,替换那些符合正则表达式的文本,许多语言都支持正则表达式 ② 使用场景 验证表单&#xff1a…

解决Adobe Flash Player已被屏蔽

问题:该插件不支持 原因:现在浏览器默认禁用flash 博主当前使用的是谷歌浏览器Chrome 2个主要方法都已经失效 搜索一圈后,之前博客给出的2个主要方法都已经失效。 1、flash.cn 下载本地播放器 2、在chrome中打开flash的禁用开关 2023年解…

vm ubuntu20共享文件夹

问题: 根据教程开启共享文件夹后 ,在ubuntu 的/mnt里面没有hgfs目录,也没有共享的两个文件夹,执行如上命令可重新挂载共享目录 vmware-hgfsclient sudo vmhgfs-fuse .host:/ /mnt/ -o nonempty -o allow_other 之后通过find / -name 你的共享目录名字 查看是否加载成功 附…

总线基本概念

总线基本概念 为什么要用总线 总线是连接各个部件的信息传输线,是各个部件共享的传输介质。(如果没有总线,那么每新增一个组件,就需要增加若干线和其余组件相连) 在任何一个时刻,只能有一对设备&#xf…

第三章 图论 No.12欧拉回路与欧拉路径

文章目录 定义欧拉路径的性质:1123. 铲雪车边编号输出欧拉路径:1184. 欧拉回路点编号字典序最小输出欧拉路径:1124. 骑马修栅栏并查集判断有向图是否存在欧拉路径:1185. 单词游戏 定义 小学一笔画问题,每条边只经过一次…

pconsc4 安装

Pconsc4 安装遇到的问题 Pconsc4-github 按照红框给的一行命令,一行毁所有。 1 gcc and g not found # 1 Start by updating the packages list:sudo apt update# 2 Install the build-essential package by typing:sudo apt install build-essential## The comm…

83. 删除排序链表中的重复元素

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 解题思路:从前往后遍历节点,如果当前节点和下一个节点的值相等,就跳过下一个节点 具体算法如下: current head如果 current!null…