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

news/2024/5/9 14:52:37/文章来源:https://blog.csdn.net/llg___/article/details/132792152

文章目录

  • 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.luyixian.cn/news_show_358232.aspx

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

相关文章

【SpringMVC】Jrebel 插件实现热部署与文件上传

目录 一、JRebel 1.1 Jrebel介绍 1.2 Jrebel插件下载 1.3 Jrebel服务下载并启动 1.4 在线生成GUID 1.5 JRebel激活 1.6 相关设置 注意❗ 二、文件上传、下载 2.1 导入pom依赖 2.2 配置文件上传解析器 2.3 文件上传表单设置 2.4 文件上传实现 2.5 文件下载实现 2…

使用 crontab 定时任务使用 curl 发送请求

crontab 简单用法 crontab 一般是 linux 系统自带的 输入以下命令可以添加定时任务&#xff0c;里面有 crontab 的说明及示例 crontab -e示例格式如下 # 前面五个分别代表分、时、天、月、周&#xff0c;后面就是命令 * * * * * command例如 * * * * * command就是每分钟执行…

第7篇 vue的模块化与babel的转换

一 babel的转换 1.1 babel的转换 Babel是一个广泛使用的转码器&#xff0c;可以将ES6代码转为ES5代码&#xff0c;从而在现有环境执行执行。 可以现在就用 ES6 编写程序&#xff0c;而不用担心现有环境是否支持。 1.2 案例 1.新建工程&#xff0c;初始化&#xff1a; npm …

Jetsonnano B01 笔记6:开启USB摄像头

今日继续我的Jetsonnano学习之路&#xff0c;今日尝试开启一下USB摄像头&#xff0c;显示拍摄的内容。 测试代码是搬运的官方说明&#xff0c;这里只是作笔记记录学习&#xff1a; 目录 额外模块准备&#xff1a; 测试代码分析&#xff1a; 运行效果&#xff1a; 额外模块准…

计算机视觉-OpenCV入门讲解

&#x1f389;作者简介&#xff1a;在读计算机研究生&#xff0c;目前研二。主要研究方向是人工智能和群智能算法方向。目前熟悉python网页爬虫、机器学习、计算机视觉&#xff08;OpenCV&#xff09;、群智能算法目前正在学习深度学习的相关内容。 &#x1f4c3;个人主页&…

Brief. Bioinformatics2023 | 利用深度学习和分子动力学模拟设计抗菌肽

文章标题&#xff1a;Designing antimicrobial peptides using deep learning and molecular dynamic simulations 代码&#xff1a;https://github.com/gc-js/Antimicrobial-peptide-generation 一、问题 PandoraGAN使用手动策划的130个高活性肽的训练数据集&#xff0c;其…

python读取监控流通过websocket连接发送到java服务端,服务端推流到前端

python读取逐帧读取监控 import websocket import base64 import cv2 import numpy as npvideoPath "rtmp://ns8.indexforce.com/home/mystream" // 此为公开RTSP流def on_message(ws, message):print(1)def connection_tmp(ws):websocket.enableTrace(True)ws w…

NLP(4)--BERT

目录 一、自监督学习 二、BERT的两个问题 三、GLUE 四、BERT与Transformer的关系 五、BERT的训练方式 六、BERT的四个例子 1、语句分类&#xff08;情感分析&#xff09; 2、词性标注 3、立场分析 4、问答系统 七、BERT的后续 1、为什么预训练后的微调可以满足多…

Python:安装Flask web框架hello world

安装easy_install pip install distribute 安装pip easy_install pip 安装 virtualenv pip install virtualenv 激活Flask pip install Flask 创建web页面demo.py from flask import Flask app Flask(__name__)app.route(/) def hello_world():return Hello World! 2023if _…

微信小程序上拉触底事件

一、什么是上拉触底事件 上拉触底是移动端的专有名词&#xff0c;通过手指在屏幕上的上拉滑动操作&#xff0c;从而加载更多数据的行为。 二、监听上拉触底事件 在页面的.js文件中&#xff0c;通过onReachBottom()函数即可监听当前页面的上拉触底事件。 三、配置上拉触底距…

使用perf_analyzer和model-analyzer测试tritonserver的模型性能超详细完整版

导读 当我们在使用tritonserver部署模型之后&#xff0c;通常需要测试一下模型的服务QPS的能力&#xff0c;也就是1s我们的模型能处理多少的请求&#xff0c;也被称为吞吐量。 测试tritonserver模型服务的QPS通常有两种方法&#xff0c;一种是使用perf_analyzer 来测试&#…

《React vs. Vue vs. Angular:2023年的全面比较》

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

SSM - Springboot - MyBatis-Plus 全栈体系(四)

第二章 SpringFramework 四、SpringIoC 实践和应用 1. SpringIoC / DI 实现步骤 1.1 配置元数据&#xff08;配置&#xff09; 配置元数据&#xff0c;既是编写交给SpringIoC容器管理组件的信息&#xff0c;配置方式有三种。基于 XML 的配置元数据的基本结构&#xff1a; …

基于堆叠⾃编码器的时间序列预测 深层神经网络

自适应迭代扩展卡尔曼滤波算法&#xff08;AIEK&#xff09;是一种滤波算法&#xff0c;其目的是通过迭代过程来逐渐适应不同的状态和环境&#xff0c;从而优化滤波效果。 该算法的基本思路是在每一步迭代过程中&#xff0c;根据所观测的数据和状态方程&#xff0c;对滤波器的…

13-RocketMQ主从同步(HA实现)源码原理

slave每次接收到master发过来的一批commitlog数据时&#xff0c;会看master传过来的这段commitlog的起始端&#xff0c;对应的全局物理偏移量&#xff0c;和slave本地存储的批commitlog数据的最大物理偏移量&#xff0c;是否相等 如果相等&#xff0c;也说明master端没有给sla…

系统架构设计专业技能 · 软件工程之UML建模设计

现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everything is for the future of dream weaving wings, let the dream fly in reality. 点击进入系列文章目录 系统架构设计高级技能 软件工程之UML建模设计 一、需求分析 - UML图二、用例图2.…

十五、Webpack打包图片-js-Vue、Label命令、resolve模块解析

一、webpack打包图片 &#xff08;1&#xff09;加载图片案例准备 为了演示我们项目中可以加载图片&#xff0c;我们需要在项目中使用图片&#xff0c;比较常见的使用图片的方式是两种&#xff1a; img元素&#xff0c;设置src属性&#xff1b;其他元素&#xff08;比如div&…

【GAMES202】Real-Time Ray Tracing 1—实时光线追踪1

一、前言 这篇我们开始新的话题—Real-Time Ray Tracing简称RTRT&#xff0c;也就是实时光线追踪&#xff0c;关于光线追踪&#xff0c;我们已经不止一次提到过它的优点&#xff0c;无论是软阴影还是全局光照&#xff0c;光线追踪都很容易做&#xff0c;唯一的缺点就是速度太慢…

【新版】系统架构设计师 - 软件架构设计<SOA与微服务>

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 架构 - 软件架构设计&#xff1c;SOA与微服务&#xff1e; 考点摘要 面向服务SOA&#xff08;★★★★&#xff09;微服务&#xff08;★★★★&#xff09; 基于/面向服务的&#xff08;SOA&#xff09; 在SO…

Ubuntu23.10将推出全磁盘加密功能,提高系统安全性

Canonical 宣布其即将推出的 Ubuntu 23.10&#xff08;Mantic Minotaur&#xff09;将引入基于 TPM 的全磁盘加密的初步支持。这个特性将利用系统可信平台模块&#xff08;TPM&#xff09;&#xff0c;在系统级别上进行全磁盘加密&#xff0c;从而提高系统的安全性。 但需要注…