【Spring面试】四、Bean的生命周期、循环依赖、BeanDefinition

news2025/1/19 7:57:32

文章目录

  • Q1、Bean有哪些生命周期回调方法?有哪几种实现方式?
  • Q2、Spring在加载过程中Bean有哪几种形态
  • Q3、解释下Spring框架中Bean的生命周期
  • Q4、Spring是如何解决Bean的循环依赖的
  • Q5、Spring是如何帮我们在并发下避免获取不完整的Bean的?
  • Q6、描述下BeanDefinition的加载过程
  • Q7、如何在所有的BeanDefinition注册完成后做扩展?

Q1、Bean有哪些生命周期回调方法?有哪几种实现方式?

包括创建和销毁,实现方式分别有三种:

A1:使用@PostConstruct和@PreDestory

在这里插入图片描述

在相应的类中写初始化和销毁方法,加@PostConstruct和@PreDestory注解:

在这里插入图片描述

写个测试类:

在这里插入图片描述

A2:实现InitializingBean和DisposableBean接口

在这里插入图片描述

A3:使用init-method和destroy-method方法

在这里插入图片描述
注意这时候不适用于@Component,而是@Bean和xml:

在这里插入图片描述

最后,关于这三种方式的执行顺序,同时保留三种初始化和销毁的代码,可以看到:

在这里插入图片描述

Q2、Spring在加载过程中Bean有哪几种形态

答案:

在这里插入图片描述

  • 概念态Bean:刚使用@Bean或者<bean/>配置完各项Bean的信息
  • 定义态Bean:创建完ApplicationContext容器,概念态的信息如:id、scope、lazy等信息被读取到BeanDefinition对象中
  • 纯净态Bean:Spring容器通知BeanFactory生产,但这时只是刚实例化,没有依赖注入,先存于二级缓存里(循环依赖问题才会体现出纯净态的作用
  • 成熟态Bean:属性注入,成为一个成熟态Bean,加入到单例池(一个Map,也叫一级缓存)

Q3、解释下Spring框架中Bean的生命周期

答案:

Bean的生命周期,指的就是Bean从创建到销毁的整个过程,主要有四大步:

STEP1实例化

  • 通过反射去拿到构造函数来(new Instance)实例化
  • 当然实例化也可能是实例工厂、静态工厂等

STEP2属性赋值

  • 解析自动装配(DI的体现),可byName、byType、constractor
  • 这里当然还有循环依赖的情况

STEP3初始化

  • 调用那些XXXAware的回调方法
  • 调用初始化生命周期的回调方法(init-method)
  • 如果Bean涉及了AOP,还要创建动态代理

STEP4销毁

  • 在Spring容器关闭的时候调用
  • 调用销毁生命周期的回调方法(destroy-method)

Q4、Spring是如何解决Bean的循环依赖的

在这里插入图片描述
如上图,实例化完BeanA,要属性注入了,此时发现需要依赖Bean B,在IoC容器中找Bean B,没找到,开始创建Bean B,实例化完做DI发现需要Bean A,结果在IoC中没找到Bean A,于是就开始重复上面的这个流程,形成死循环。

答案:

Spring是采用三级缓存来解决循环依赖的,三级缓存分别是三个Map

  • 一级缓存是用来存储完整的Bean的
  • 二级缓存,存半成品的Bean,避免多重循环依赖的情况下,重复创建动态代理
  • 三级缓存,存lambda表达式

在这里插入图片描述

  • 首先创建Bean A,此时去一级缓存中getBean(A),发现没有,则进行实例化,并加入三级缓存
  • 接下来给A属性赋值,发现依赖Bean B
  • 因此去一级缓存中getSingleton(B),找不到B,则createBean(B)
  • 实例化Bean B后并加入三级缓存,此时给B属性赋值,发现依赖A,去getSingleton(A),此时先去一级缓存,找不到,二级缓存也没有找到
  • 最终到三级缓存,此时第二次调用getBean(A)了,就会调用三级缓存,并将结果保存到二级缓存

在这里插入图片描述

  • 此时B里的A就已经赋值完成了,虽然是个半成品的A(就好比算命的说这个人未来是你对象,人就是这个人,哪怕ta现在还未成年)

  • 到此,循环被打破,B被存储到一级缓存,addSingleton(B),并remove二级三级缓存

在这里插入图片描述

  • 将创建好的Bean B返回给A,Bean A也完成了属性赋值
  • A再进行初始化阶段,最终创建完成

关于三级缓存:

  • 三级缓存是函数接口,通过lambda把方法传进去(把Bean的实例和名字都传进去)(aop创建)
  • 不会立即调用
  • 会在ABA,即创建B时第二次getBean(A)才调用三级缓存(如果实现了aop,就会创建动态代理,如果没有,依然返回Bean的实例)
  • 结果会放入二级缓存,避免再有Bean C也依赖A时重复创建
ps:为什么不在实例化A后直接放进一级缓存的Map中去?
--------
- 因为此时A尚未创建完整,所有属性都是默认值,并不是一个完整的对象
- 如果直接扔进一级缓存,在执行业务时可能会抛出未知的异常

相关问题:

Q4.1、二级缓存能不能解决循环依赖?
  • 如果只是想跳出这个死循环问题,那一级缓存就可以解决(实例化后直接扔一级缓存),但这样无法避免并发下获取不到完整的Bean
  • 二级缓存也可以解决循环依赖,只不过如果出现重复的循环依赖,即ABA、ACA,就会多次创建AOP的动态代理
Q4.2Spring有没有解决多例Bean的循环依赖?
  • 多例Bean,不同的Bean,创建完也不会存到一级缓存、二级缓存、三级缓存中,不会使用缓存进行存储,因为每次使用都会重新创建
  • 不缓存早期形态的对象,就无法解决循环问题(需要一个缓存保存早期形态的对象,来做为死循环的出口打破循环)
Q4.3Spring有没有解决构造函数参数Bean的循环依赖?
  • 构造函数里的循环依赖也会报错
  • 可以通过@Lazy注解解决,使用@Lazy就不会立即创建所依赖的Bean了,而是等到用到,才通过动态代理进行创建
    在这里插入图片描述

Q5、Spring是如何帮我们在并发下避免获取不完整的Bean的?

问题分析:

不完整的Bean,即只是完成了实例化,没有完成属性赋值(DI)和初始化(生命周期回调)。其次,并发下,假如有两个线程,都来getBean:

在这里插入图片描述
线程1以微小优势先来创建bean,实例化后放进三级缓存,此时,没有属性赋值和初始化,线程2进来getBean,getSinfleton去三个缓存中找,就拿到了不完整的Bean。

答案:

双重检查锁,2个同步锁,2次检查一级缓存

在这里插入图片描述

  • 线程1进来,先在缓存中获取,没获取到,先给缓存加锁(一级缓存未加锁)
  • 接下来进行实例化、属性赋值等操作,并放入三级缓存,这些过程也加锁
  • 此时线程2进来,getBean(A) -> doGetBean(A) -> getSingleton(A,boolean)
  • 先去一级缓存获取,一级只存完整的Bean,自然获取不到
  • 此时想去二级、三级缓存获取,但二三级被加锁了,线程2阻塞
  • 直到线程1走完流程,得到一个完整的Bean,并放到一次缓存,remove二三级缓存,返回一个null
  • 线程1释放锁
  • 线程2去二三级缓存获取到null,此时并不是新创建,二是再调用getSingleton,第二次去一级缓存(即双重检查)
  • 这次自然获取到了一个完整的Bean

连问:为什么不给一级缓存加锁,一次缓存要是加锁,则直接阻塞在一级缓存等待结果,也不用二次检查了。

答案:

因为性能,加入线程2除了获取Bean A还获取Bean C,而Bean C已经创建好了,存在于一级缓存,如此就会导致已经创建好的Bean阻塞等待。

Q6、描述下BeanDefinition的加载过程

BeanDefinition:用来存放(定义)Bean的生产信息,决定Bean如何进行生产(定义态的Bean)

答案:

  • 创建Spring容器
  • BeanDefinitionReader读取配置
  • 配置类的解析器开始解析:@Bean、@Import、@Component…
  • 如果解析ComponentScan,则先找到包路径下的所有.class文件,判断类是不是标准的@Component(排除接口,抽象类)
  • 解析完后,注册BeanDefintion
  • BeanDefinition加入到BeanDefinitionMap,交给后面的BeanFactory来生产

在这里插入图片描述

Q7、如何在所有的BeanDefinition注册完成后做扩展?

回顾下之前的这张图:

在这里插入图片描述
重点就在BeanDefinition后置处理器这里:

在这里插入图片描述

创建Spring上下文对象(IoC容器时),源码中调用refresh方法,这个方法内部就调用了invokeBeanFactroyPostProcessors,去注册所有的BeanDefinition:

在这里插入图片描述
而接下来就会调用图中的BeanFactoryPostProcessor,就是修改BeanDefinition的时机,所以我们只需实现这个BeanFactoryPostProcessor接口即可,Spring就会去调用:

答案:

实现Bean工厂后置处理器接口BeanFactoryPostProcessor,即可在所有的BeanDefinition注册完成后做扩展

在这里插入图片描述

写个测试程序:

在这里插入图片描述

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

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

相关文章

LeetCode 729. My Calendar I【设计;有序集合,二分查找;线段树】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

Single View Point Omnidirectional Camera Calibration from Planar Grids

参考论文 paper里面最关键的部分为这段 实际相当于有一个xi的参数要使用, 和正常的相机内参定义不太一样, 那么在3d往相机上投的时候, 是这样的 也参考: https://github.com/autonomousvision/kitti360Scripts/blob/master/kitti360scripts/helpers/project.py#L174-L198

TDesign数据请求wx.request

wxml页面如下&#xff1a; bind:tap绑定了handleAjax事件&#xff0c;js页面如下&#xff1a; 点击按钮&#xff0c;运行成功输出data 报错&#xff1a; 小程序 (qq.com)https://mp.weixin.qq.com/ 再次点击按钮ajax 运行成功&#xff1a;

JTAG 简介

文章目录 1、JTAG 基本原理1.1、JTAG接口包括以下几个信号&#xff1a;1.2、The Debug TAP State Machine (DBGTAPSM) 2、JTAG 的应用 1、JTAG 基本原理 JTAG是Joint Test Action Group的缩写&#xff0c;它是一种国际标准测试协议&#xff0c;主要用于芯片或印制电路板的边界…

SpringBoot系列(12):SpringBoot集成log4j2日志配置

最近项目上有使用到log4j2日志模板配置&#xff0c;本文简单总结一下之前的学习笔记&#xff0c;如有纰漏之处&#xff0c;请批评指正。 1. log4j2日志依赖 使用log4j2日志模板时&#xff0c;需要引入相关依赖&#xff0c;下边的两种依赖方式均可。 1.1 使用sl4j依赖时 <…

kafka学习-消费者

目录 1、消费者、消费组 2、心跳机制 3、消费者常见参数配置 4、订阅 5、反序列化 基本概念 自定义反序列化器 6、位移提交 6.1、自动提交 6.2、手动提交 同步提交 异步提交 7、再均衡 7.1、定义与基本概念 7.2、缺陷 7.3、如何避免再均衡 7.4、如何进行组内分…

React+Typescript+react-router 6 创建路由操作

本文我们来看看路由的安装 其实路由的操作没有什么变化 但是还是给大家讲一下 那么我们打开项目 在项目终端输入 npm install --save react-router react-router-dom安装 一下 react-router 和 react-router-dom 这都是react开发很基本的插件了 不过大家安装前先注意好我的版…

C++ 进制转化入门知识(1)

一、什么是进制 进制是一种用来表示数值的系统或方法&#xff0c;它是基于一个特定的基数来工作的。在我们常见的几种进制中&#xff0c;有&#xff1a; 1. **二进制&#xff08;基数 2&#xff09;**&#xff1a; 二进制只用两个数字&#xff1a;0和1。这是计算机内部使用…

GB28181学习(三)——心跳保活

心跳保活 要求&#xff1a; 1. 当原设备发现工作异常时&#xff0c;应立即向本SIP监控域的SIP服务器发送状态信息&#xff1b; 2. 无异常时&#xff0c;定时向本SIP监控域的SIP服务器发送状态信息&#xff1b; 3. 状态信息报送采用**MESSGAE**方法&#xff1b; 4. SIP设备宜在…

不同温度与工况的放电曲线与内阻曲线

在电动汽车中&#xff0c;机器学习被广泛应用于许多领域&#xff0c;包括电池状态估计。电池的状态 of charge (SOC) 是电池中可用能量的百分比。准确估计SOC对于优化电池性能、延长电池寿命和维护安全性至关重要。然而&#xff0c;SOC估计是一个复杂的任务&#xff0c;因为电池…

线性规划对偶问题:理论推导和实际应用

文章目录 对偶问题实例对偶问题定义和性质定义性质 对偶问题应用影子价格理论应用 参考文献 对偶问题实例 之前在很多地方&#xff0c;都看到过“对偶”这两个字眼&#xff0c;总觉得这个词很高大上。对偶理论的百度百科中甚至写到&#xff1a;“在线性规划早期发展中最重要的…

Unity之创建第一个2D游戏项目

一 Unity环境配置 1.1 Untity资源官网下载&#xff1a;https://unity.cn/releases 1.2 Unity Hub集成环境&#xff0c;包含工具和项目的管理 1.3 Unity Editor编辑器 1.4 Visual Studio 2022脚本编辑器 1.5 AndroidSKD&#xff0c;JDK&#xff0c;NDK工具&#xff0c;用于and…

tcp连接+套接字编程

tcp头部 tcp端口号 TCP的连接是需要四个要素确定唯一一个连接&#xff1a;&#xff08;源IP&#xff0c;源端口号&#xff09; &#xff08;目地IP&#xff0c;目的端口号&#xff09; 所以TCP首部预留了两个16位作为端口号的存储&#xff0c;而IP地址由上一层IP协议负责传递 源…

autoware.ai感知随笔--地面滤波

autwoware.ai中点云预处理–points_preprocessor points_preprocessor cloud_transformer: 点云坐标转换,将输入的点云转化为velodyne坐标系下的点云。 compare_map_filter: 对比激光雷达点云和点云地图&#xff0c;然后提取&#xff08;或去除&#xff09;一致的点。 |input_…

联通面试题

一、GC 1.1、目标 GC的主要作用是自动识别和释放不再使用的对象&#xff0c;回收其所占用的内存&#xff0c;以防止内存泄漏和内存溢出的问题。 1.2、如何实现 1.2.1、标记阶段 GC从根对象&#xff08;如线程栈中的引用、静态变量等&#xff09;开始&#xff0c;通过可达性…

CnosDB 签约京清能源,助力分布式光伏发电解决监测系统难题。

近日&#xff0c;京清能源采购CnosDB&#xff0c;升级其“太阳能光伏电站一体化监控平台”。该平台可以实现电站设备统一运行监控&#xff0c;数据集中管理&#xff0c;为操作人员、维护人员、管理人员提供全面、便捷、差异化的数据和服务。 京清能源集团有限公司&#xff08;…

【LeetCode】35.复杂链表的复制

题目 请实现 copyRandomList 函数&#xff0c;复制一个复杂链表。在复杂链表中&#xff0c;每个节点除了有一个 next 指针指向下一个节点&#xff0c;还有一个 random 指针指向链表中的任意节点或者 null。 示例 1&#xff1a; 输入&#xff1a;head [[7,null],[13,0],[11,4]…

并发-Executor框架笔记

Executor框架 jdk5开始&#xff0c;把工作单元与执行机制分离开来&#xff0c;工作单元包括Runable和Callable&#xff0c;执行机制由Executor框架来提供。 Executor框架简介 Executor框架的两级调度模型 Java线程被一对一映射为本地操作系统线程 java线程启动会创建一个本…

Linux单列模式实现线程池

目录 一、单列模式 1.1 单列模式概念以及实现条件 1.2 饿汉模式 1.1.1 饿汉模式代码实现 1.1.2 饿汉模式特征和优缺点 1.3 懒汉模式 1.3.1 懒汉模式代码实现 1.3.2 懒汉模式特征以及优缺点 二、线程池 2.1 线程池概念 2.2 实现简单线程池逻辑 2.3 模拟实现懒汉模式线程…

【八大经典排序算法】:直接插入排序、希尔排序实现 ---> 性能大比拼!!!

【八大经典排序算法】&#xff1a;直接插入排序、希尔排序实现 ---> 性能大比拼&#xff01;&#xff01;&#xff01; 一、 直接插入排序1.1 插入排序原理1.2 代码实现1.3 直接插入排序特点总结 二、希尔排序 ( 缩小增量排序 )2.1 希尔排序原理2.2 代码实现2.3 希尔排序特点…