SpringBean的生命周期和循环依赖

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.rhkb.cn/news/89359.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习笔记-JVM-工具包(JVM分析工具)

常用工具 JDK工具 ① jps: JVM Process status tool:JVM进程状态工具,查看进程基本信息 ② jstat: JVM statistics monitoring tool : JVM统计监控工具,查看堆,GC详细信息 ③ jinfo:Java Configuration I…

Mysql 和Oracle的区别

、mysql与oracle都是关系型数据库,Oracle是大型数据库,而MySQL是中小型数据库。但是MySQL是开源的,但是Oracle是收费的,而且比较贵。 1 2 mysql默认端口:3306,默认用户:root oracle默认端口&…

Observability:识别生成式 AI 搜索体验中的慢速查询

作者:Philipp Kahr Elasticsearch Service 用户的重要注意事项:目前,本文中描述的 Kibana 设置更改仅限于 Cloud 控制台,如果没有我们支持团队的手动干预,则无法进行配置。 我们的工程团队正在努力消除对这些设置的限制…

学会智慧工地有多爽?能省时间又高效?

当今社会,科技的迅速发展正在深刻地改变着各行各业,建筑领域也不例外。在这一背景下,"智慧工地"这一概念应运而生,它代表了将创新技术和数字化解决方案引入建筑工地,以提升效率、安全性和可持续性的愿景。 智…

阿里云Alibaba Cloud Linux镜像系统介绍_常见问题解答FAQ

阿里云服务器操作系统Alibaba Cloud Linux镜像怎么样?可以代替CentOS吗?Alibaba Cloud Linux兼容性如何?有人维护吗?漏洞可以修复吗?Alibaba Cloud Linux完全兼容CentOS,并由阿里云官方免费提供长期维护。 …

LinuxC编程——进程

目录 一、概念1.1 程序1.2 进程 二、特点⭐⭐⭐三、进程段四、进程分类五、进程状态六、进程状态转换图七、函数接口1. 创建子进程2. 回收进程资源3. 退出进程4. 获取进程号 八、守护进程 一、概念 进程和程序是密不可分的两组概念,相对比,便于理解。 1.…

【计算机网络】Udp详解

前言 上几文章我们讲解了应用层协议Http和Https,要知道应用层协议有很多,这些都是程序员自己定制的,而真正要传输的时候,是要在操作系统的传输层进行的,今天我们就来学习一下传输层协议Udp的 标识一个通信 要进行跨…

uni-app微信小程序开发自定义select下拉多选内容篇

分享-2023年资深前端进阶:前端登顶之巅-最全面的前端知识点梳理总结 *分享一个使用比较久的🪜 技术框架公司的选型:uni-app uni-ui vue3 vite4 ts 需求分析:微信小程序-uni-ui内容 1、创建一个自定义的下拉,支持多…

【Kubernetes】Kubernetes的调度

K8S调度 一、Kubernetes 调度1. Pod 调度介绍2. Pod 启动创建过程3. Kubernetes 的调度过程3.1 调度需要考虑的问题3.2 具体调度过程 二、影响kubernetes调度的因素1. nodeName2. nodeSelector3. 亲和性3.1 三种亲和性的区别3.2 键值运算关系3.3 节点亲和性3.4 Pod 亲和性3.5 P…

React - useEffect函数的理解和使用

文章目录 一,useEffect描述二,它的执行时机三,useEffect分情况使用1,不写第二个参数 说明监测所有state,其中一个变化就会触发此函数2,第二个参数如果是[]空数组,说明谁也不监测3,第…

分享一组天气组件

先看效果&#xff1a; CSS部分代码&#xff08;查看更多&#xff09;&#xff1a; <style>:root {--bg-color: #E9F5FA;--day-text-color: #4DB0D3;/* 多云 */--cloudy-background: #4DB0D3;--cloudy-temperature: #E6DF95;--cloudy-content: #D3EBF4;/* 晴 */--sunny-b…

【严重】Smartbi未授权设置Token回调地址获取管理员权限

漏洞描述 Smartbi是一款商业智能应用&#xff0c;提供了数据集成、分析、可视化等功能&#xff0c;帮助用户理解和使用他们的数据进行决策。 在 Smartbi 受影响版本中存在Token回调地址漏洞&#xff0c;未授权的攻击者可以通过向目标系统发送POST请求/smartbix/api/monitor/s…

微型导轨在包棉机中的作用

随着工业革命的开展&#xff0c;各种人工智能设备的迅猛发展&#xff0c;为了适应高速发展的工业自动化&#xff0c;越来越多的工业企业开始采用微型导轨&#xff0c;尤其是在包棉机中的应用。 包棉机是一种用于加工棉花的机械设备&#xff0c;它的主要功能是将原始棉花经过清洁…

QT生成Word PDF文档

需求&#xff1a;将软件处理的结果保存为一个报告文档&#xff0c;文档中包含表格、图片、文字&#xff0c;格式为word的.doc和.pdf。生成word是为了便于用户编辑。 开发环境&#xff1a;qt4.8.4vs2010 在qt的官网上对于pdf的操作介绍如下&#xff1a;http://qt-project.org/…

微服务06-分布式事务解决方案Seata

1、Seata 概述 Seata事务管理中有三个重要的角色: TC (Transaction Coordinator) - **事务协调者:**维护全局和分支事务的状态,协调全局事务提交或回滚。 TM (Transaction Manager) - **事务管理器:**定义全局事务的范围、开始全局事务、提交或回滚全局事务。 RM (Resourc…

npm run xxx 的时候发生了什么?(以npm run dev举例说明)

文章目录 一、去package.json寻找scripts对应的命令二、去node_modules寻找vue-cli-service三、从package-lock.json获取.bin的软链接1. bin目录下的那些软连接存在于项目最外层的package-lock.json文件中。2.vue-cli-service文件的作用3.npm install 的作用 总结 一、去packag…

微服务04-elasticsearch

1、es概念 1.1 文档和字段 elasticsearch是面向**文档(Document)**存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化为json格式后存储在elasticsearch中: 而Json文档中往往包含很多的字段(Field),类似于数据库中的列。 1.2 索引和映射 索引(…

【hello C++】特殊类设计

目录 一、设计一个类&#xff0c;不能被拷贝 二、设计一个类&#xff0c;只能在堆上创建对象 三、设计一个类&#xff0c;只能在栈上创建对象 四、请设计一个类&#xff0c;不能被继承 五、请设计一个类&#xff0c;只能创建一个对象(单例模式) C&#x1f337; 一、设计一个类&…

【设计模式】单例模式

单例模式&#xff08;Singleton Pattern&#xff09;是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类&#xff0c;该类负责创建自己的对象&#xff0c;同时确保只有单个对象被创建…