Spring Bean的生命周期各阶段详解附源码

目录

  • Bean的生命周期
  • Bean定义阶段
  • Bean实例化阶段
  • Bean属性注入阶段
  • Bean初始化阶段
  • Bean销毁阶段

Bean的生命周期

bean的生命周期,我们都知道大致是分为:bean定义,bean的实例化,bean的属性注入,bean的初始化以及bean的销毁这几个过程

然而在bean创建和初始化的过程会有很多自定义的钩子,我们可以去实现继承,对bean做自定义的操做。下面看各个阶段的详解。
在这里插入图片描述


Bean定义阶段

bean定义阶段,也叫bean声明,这个不是很重要,甚至都不算bean的生命周期,但是它是bean的起始,我们熟知的就是通过配置xml文件,在spring的配置文件中通过< bean >标签来声明,或者是通过boot中的注解方式来声明

一个会在启动的时候读取xml文件,由DefaultBeanDefinitionDocumentReader(这个其实不重要,就是读取配置文件并解析的)进行解析文件,根据bean标签中配置的包名给定义成BeanDefinitions注册到singletonObjects缓存池中,等待getBean的时候进行初始化再缓存到IOC容器中

另一个注解的方式,是再启动时会通过它的@CompontScan注解来进行扫包,获取对应类上注解标明的bean,然后将其的类信息封装成BeanDefinitions注册到singletonObjects缓存池中


Bean实例化阶段

bean实例化阶段会通过反射的方式来构建bean实例
创建bean的过程,其逻辑流程图如下所示;因为我们调用的getBean方法,其内部是调用的doGetBean
在这里插入图片描述

这个是创建bean 的源码部分

//省略许多异常处理的部分
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {//初始化阶段的args == nullRootBeanDefinition mbdToUse = mbd;// 确保 BeanDefinition 中的 Class 被加载Class<?> resolvedClass = resolveBeanClass(mbd, beanName);if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);}// 准备方法覆写它来自于 bean 定义中的 <lookup-method /> 和 <replaced-method />mbdToUse.prepareMethodOverrides();// 让 InstantiationAwareBeanPostProcessor 在这一步有机会返回代理Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean;// 重头戏,创建 beanObject beanInstance = doCreateBean(beanName, mbdToUse, args);return beanInstance;
}

主要还是通过doCreateBean来实现,所以继续进入doCreateBean方法,Bean的实例化+初始化都在这一步中完成。
doCreateBean方法进去比较长,这里也不方便把所有代码直接放上来,只把它存在三个重要关键方法给一一列举


createBeanInstance 创建实例
这个是第一个

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {//确保已经加载了这个classClass<?> beanClass = resolveBeanClass(mbd, beanName);//校验这个类的访问权限if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {throw new BeanCreationException();}//spring5.0 返回创建bean实例的回调Supplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}if (mbd.getFactoryMethodName() != null) {//采用工厂方法实例化return instantiateUsingFactoryMethod(beanName, mbd, args);}// 如果是第二次创建 如prototype bean,这种情况下,我们可以从第一次创建知道,采用无参构造函数,还是构造函数依赖注入 来完成实例化boolean resolved = false;boolean autowireNecessary = false;if (args == null) {synchronized (mbd.constructorArgumentLock) {if (mbd.resolvedConstructorOrFactoryMethod != null) {resolved = true;autowireNecessary = mbd.constructorArgumentsResolved;}}}if (resolved) {if (autowireNecessary) {//构造函数注入return autowireConstructor(beanName, mbd, null, null);}else {//无参构造函数return instantiateBean(beanName, mbd);//重点这个,下面做了解释说明}}// 判断是否采用有参构造函数Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {//args!=null 的构造函数注入(有参)return autowireConstructor(beanName, mbd, ctors, args);}// Preferred constructors for default construction?ctors = mbd.getPreferredConstructors();if (ctors != null) {//判断是否采用首选的构造函数return autowireConstructor(beanName, mbd, ctors, null);}// 调用无参构造函数return instantiateBean(beanName, mbd);
}

以无参构造函数为例,实例化的过程在SimpleInstantiationStrategy中。
最后通过反射的方式进行的实例化,就是上面代码的无参构造

public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {// 如果不存在方法覆写,就是用java的反射进行实例化, 否则使用CGLIBif (!bd.hasMethodOverrides()) {Constructor<?> constructorToUse;synchronized (bd.constructorArgumentLock) {constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;if (constructorToUse == null) {final Class<?> clazz = bd.getBeanClass();if (clazz.isInterface()) {throw new BeanInstantiationException(clazz, "Specified class is an interface");}try {if (System.getSecurityManager() != null) {constructorToUse = AccessController.doPrivileged((PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);}else {constructorToUse = clazz.getDeclaredConstructor();}bd.resolvedConstructorOrFactoryMethod = constructorToUse;}catch (Throwable ex) {throw new BeanInstantiationException(clazz, "No default constructor found", ex);}}}//利用构造方法进行实例化return BeanUtils.instantiateClass(constructorToUse);}else {// 存在方法覆写的情况,需要利用CGLIB来完成实例化,需要依赖于CGLIB生成子类return instantiateWithMethodInjection(bd, beanName, owner);}
}

Bean属性注入阶段

populateBean 填充属性,这个方法是前面说doCreateBean的三个方法之一
这个是第二个

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {if (bw == null) {if (mbd.hasPropertyValues()) {//this.propertyValues bean实例的所有属性throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");}else {// Skip property population phase for null instance.return;}}//在设置属性之前,给所有InstantiationAwareBeanPostProcessor机会修改bean的状态// 【此时bean的状态 = 已经通过工厂方法或者构造方法实例化,在属性赋值之前】。例如,可以使用支持字段注入的样式。InstantiationAwareBeanPostProcessorif (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return;}}}PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);//获取PropertyValue对象int resolvedAutowireMode = mbd.getResolvedAutowireMode();if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {//获取Autowire的模式  or 通过名字, or 通过类型MutablePropertyValues newPvs = new MutablePropertyValues(pvs);// 通过名字找到所有属性值,如果是 bean 依赖,先初始化依赖的 bean。记录依赖关系if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {autowireByName(beanName, mbd, bw, newPvs);}// 通过类型装配 记录依赖关系if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {autowireByType(beanName, mbd, bw, newPvs);}pvs = newPvs;}//...省略//设置bean实例的属性值if (pvs != null) {applyPropertyValues(beanName, mbd, bw, pvs);}
}

由populateBean方法向下的流程图如下所示

在这里插入图片描述


Bean初始化阶段

initializeBean 回调方法
这个是第三个,属性注入完成,处理各种回调,如BeanNameAware、BeanClassLoaderAware、BeanFactoryAware等

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {//if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {invokeAwareMethods(beanName, bean);return null;}, getAccessControlContext());}else {invokeAwareMethods(beanName, bean);//如果bean实现了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口, 回调}Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);//BeanPostProcessor 的 postProcessBeforeInitialization 回调}try {invokeInitMethods(beanName, wrappedBean, mbd);//处理bean中定义的init-method或 bean实现了InitializingBean ,调用afterPropertiesSet() 方法}catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName, "Invocation of init method failed", ex);}if (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);//BeanPostProcessor 的 postProcessAfterInitialization 回调}return wrappedBean;
}

在继populateBean属性注入之后也就是初始化过程,流程如下所示
在这里插入图片描述


Bean销毁阶段

在DisposableBeanAdapter.java类中,它的destroy方法中

@Override
public void destroy() {//CommonAnnotationBeanPostProcessorc 处理@preDetroyif (!CollectionUtils.isEmpty(this.beanPostProcessors)) {for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {processor.postProcessBeforeDestruction(this.bean, this.beanName);}}if (this.invokeDisposableBean) {DisposableBean的destroy方法((DisposableBean) this.bean).destroy();}}if (this.destroyMethod != null) {//destroy-method方法invokeCustomDestroyMethod(this.destroyMethod);}else if (this.destroyMethodName != null) {Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);if (methodToInvoke != null) {invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));}}
}

如有遗漏之处望多多包涵,后续继续补充完善,感谢您的阅览!

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

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

相关文章

基础课14——语音识别

ASR 是自动语音识别&#xff08;Automatic Speech Recognition&#xff09;的缩写&#xff0c;是一种将人类语音转换为文本的技术。ASR 系统可以处理实时音频流或已录制的音频文件&#xff0c;并将其转换为文本。它是一种自然语言处理技术&#xff0c;广泛应用于许多领域&#…

卷积神经网络(CNN):乳腺癌识别.ipynb

文章目录 一、前言一、设置GPU二、导入数据1. 导入数据2. 检查数据3. 配置数据集4. 数据可视化 三、构建模型四、编译五、训练模型六、评估模型1. Accuracy与Loss图2. 混淆矩阵3. 各项指标评估 一、前言 我的环境&#xff1a; 语言环境&#xff1a;Python3.6.5编译器&#xf…

VQD视频质量诊断服务/图像质量诊断/视频流质量诊断/传统方法与深度学习结合的视频质量诊断

随着平安城市、大安防的发展&#xff0c;监控摄像机数量的不断增加&#xff0c;给监控系统的维护工作带来了新的挑战。如何及时了解前端视频设备的运行情况&#xff0c;发现故障并检测恶意遮挡与破坏的不法行为已成为视频监控系统运行的首要迫切问题。对于成千上万个监控摄像机…

TCP 半连接队列和全连接队列

在 TCP 三次握手的时候&#xff0c;Linux 内核会维护两个队列&#xff0c;分别是&#xff1a; 半连接队列&#xff0c;也称 SYN 队列&#xff1b; 全连接队列&#xff0c;也称 accept 队列&#xff1b; 服务端收到客户端发起的 SYN 请求后&#xff0c;内核会把该连接存储到半连…

java 多种验证码

java 多种验证码 1.SpringBoot 引入jar包2. java 导入jar包3. 代码4. 效果图 1.SpringBoot 引入jar包 <dependency><groupId>com.github.whvcse</groupId><artifactId>easy-captcha</artifactId><version>1.6.2</version> </dep…

Spring Boot中使用Swagger

1. 启用Swagger 1.1 启用注解扫描和文档接口 直接在POM文件引入依赖 <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version> </dependency>1.2 启动swagger-u…

【Windows下】Eclipse 尝试 Mapreduce 编程

文章目录 配置环境环境准备连接 Hadoop查看 hadoop 文件 导入 Hadoop 包创建 MapReduce 项目测试 Mapreduce 编程代码注意事项常见报错 配置环境 环境准备 本次实验使用的 Hadoop 为 2.7.7 版本&#xff0c;实验可能会用到的文件 百度网盘链接&#xff1a;https://pan.baidu…

Shopee过期的折扣活动如何删除?Shopee促销商品如何下架?——站斧浏览器

商家们可以轻松删除虾皮过期活动以及下架促销商品&#xff0c;保持店铺的整洁和顾客的购物体验。那么shopee过期的折扣活动如何删除&#xff0c;shopee促销商品如何下架。 Shopee过期的折扣活动如何删除&#xff1f; 在删除虾皮过期活动时&#xff0c;商家们需要遵循以下步骤…

Deployment脚本部署Tomcat集群:外部访问、负载均衡、文件共享及集群配置调整

文章目录 前置知识一、Deployment脚本部署Tomcat集群二、外部访问Tomcat集群三、利用Rinted对外提供Service负载均衡支持1、创建服务2、端口转发工具Rinetd3、定义jsp文件查看转发到哪个节点 四、部署配置挂载点五、基于NFS实现集群文件共享1、master2、node3、验证 六、集群配…

Linux 进程

文章目录 进程定义进程的描述查看进程方法进程状态进程优先级进程相关概念补充 进程定义 大多数的说法&#xff1a;进程是计算机中正在运行的程序的实例。它是操作系统对程序的一种抽象&#xff0c;用于管理和调度程序的执行。 个人理解: 从OS(操作系统)开始说起&#xff0c;…

用户态和内核态

实际上任何Linux发行版(Centos/RedHat....)&#xff0c;其系统内核都是Linux。我们的应用都需要通过Linux内核与硬件交互。为了避免用户应用导致冲突甚至内核崩溃&#xff0c;用户应用与内核是分离的&#xff1a; 进程的寻址空间会划分为两部分&#xff1a;内核空间、用户空间。…

记一次引入低版本包导致包冲突,表现为NoClassDefFoundError的故障

简而言之&#xff0c;因为参考别的项目处理excel的代码if(org.apache.poi.hssf.usermodel.HSSFDateUtil.isCellDateFormatted(cell)) &#xff0c;为了使用这个HSSFDateUtil类我引入了依赖&#xff1a; <dependency><groupId>org.apache.poi</groupId><a…

LED恒流开关调节器FP7123,提供稳定电流,提升LED产品效果!

目录 一、FP7123概述 二、FP7123功能 LED恒流开关调节器FP7123的优势不仅仅在于提供稳定的电流&#xff0c;还包括以下几个方面&#xff1a; 三、应用领域 随着科技的不断发展&#xff0c;LED照明产品已经成为人们生活中不可或缺的一部分。然而&#xff0c;LED的亮度和稳定性…

二维码智慧门牌管理系统升级解决方案:存疑地址轻松管理

文章目录 前言一、存疑地址的统一管理二、数据查询、导出和编辑功能三、提交地址审核机制 前言 随着二维码智慧门牌管理系统在企业中的广泛应用&#xff0c;地址存疑成为了一个亟待解决的问题。为了帮助企业有效管理这些存疑地址&#xff0c;我们推出了升级解决方案&#xff0…

解雇 Sam Altman 的背后故事;梦露转 180°秒变爱因斯坦丨 RTE 开发者日报 Vol.98

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE &#xff08;Real Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

Javaweb之Vue路由的详细解析

5 Vue路由 5.1 路由介绍 将资代码/vue-project(路由)/vue-project/src/views/tlias/DeptView.vue拷贝到我们当前EmpView.vue同级&#xff0c;其结构如下&#xff1a; 此时我们希望基于4.4案例中的功能&#xff0c;实现点击侧边栏的部门管理&#xff0c;显示部门管理的信息&am…

如何安装Wnmp并结合内网穿透实现外网远程访问内网服务

文章目录 前言1.Wnmp下载安装2.Wnmp设置3.安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4.固定公网地址访问 前言 WNMP是Windows系统下的绿色NginxMysqlPHP环境集成套件包&#xff0c;安装完成后即可得到一个Nginx MyS…

抖音集团面试挂在2面,复盘后,决定二战.....

先说下我基本情况&#xff0c;本科不是计算机专业&#xff0c;现在是学通信&#xff0c;然后做图像处理&#xff0c;可能面试官看我不是科班出身没有问太多计算机相关的问题&#xff0c;因为第一次找工作&#xff0c;字节的游戏专场又是最早开始的&#xff0c;就投递了&#xf…

Android 实现环形进度条

一、项目需求 项目中常常需要用到进度条&#xff0c;很简单&#xff0c;这儿做一个简单的总结和实现 二、实现控件 ProgressBar 三、实现代码 1、水平的进度条 xml布局代码&#xff1a; <ProgressBarandroid:id"id/rocketProgressBar"style"style/Wid…

go语言使用代码示例

在Go语言中&#xff0c;使用HTTP代码示例可以帮助我们发送HTTP请求并处理HTTP响应。下面是一个简单的示例&#xff0c;演示了如何使用Go语言发送HTTP GET请求并处理响应。 go复制代码 package main import ( "fmt" "io/ioutil" "…