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));}}
}
如有遗漏之处望多多包涵,后续继续补充完善,感谢您的阅览!
相关文章:
Spring Bean的生命周期各阶段详解附源码
目录 Bean的生命周期Bean定义阶段Bean实例化阶段Bean属性注入阶段Bean初始化阶段Bean销毁阶段 Bean的生命周期 bean的生命周期,我们都知道大致是分为:bean定义,bean的实例化,bean的属性注入,bean的初始化以及bean的销毁…...
LoadBalancer将服务暴露到外部实现负载均衡Openelb-layer2模式配置介绍
目录 一.openelb简介 二.主要介绍layer2模式 1.简介 2.原理 3.部署 (1)先在集群master上开启kube-proxy的strictARP (2)应用下载openelb.yaml(需要修改镜像地址) (3)编写yam…...
Android异步之旅:探索IntentService
1.介绍IntentService IntentService是Android中的一个Service类,用于在后台执行耗时操作,而不会阻塞UI线程。它封装了HandlerThread和Handler,使得我们可以方便地在后台执行任务,而不需要自己管理线程和消息处理。 以下是 Intent…...
131.类型题-计算数学序列的和,请编写函数fun,其功能是S=……【满分解题代码+详细分析】(数学序列的和类型题-C/C++JavaPython实现)
文章目录 131.类型题-计算数学序列的和:计算并输出一.题目1.1 解题思路二.解题代码2.1 C/C++解题代码2.2 python解题代码2.3 Java解题代码三.解题代码仔细分析3.1 C/C++解题代码仔细分析3.2 Java解题代码仔细分析3.3 Python解题代码仔细分析四.本类型题解题诀窍五.寄语131.类型…...
【Unity动画】状态机中层的融合原理与用法详解
1. 状态机概念介绍 在Unity中,动画状态机(Animator State Machine)是一种强大的工具,用于控制游戏对象的动画行为。动画状态机由多个动画状态Animation和过渡条件Transition、层组成!而层(Layersÿ…...
等保之道:从基础出发,解密网站防护的重要性
随着数字化时代的推进,网站安全问题日益凸显。网站被攻击不仅会导致信息泄漏、服务中断,还可能损害用户信任和企业声誉。为了更好地解决这一问题,我们需从等保的角度审视网站防护,全面提升网络安全水平。 等保背景 等保࿰…...
7. 系统信息与系统资源
7. 系统信息与系统资源 1. 系统信息1.1 系统标识 uname()1.2 sysinfo()1.3 gethostname()1.4 sysconf() 2. 时间、日期2.1 Linux 系统中的时间2.1.1 Linux 怎么记录时间2.1.2 jiffies 的引入 2.2 获取时间 time/gettimeofday2.2.1 time()2.2.2 gettimeofday() 2.3 时间转换函数…...
【重点】【滑动窗口】239. 滑动窗口最大值
题目 也可参考:剑指offer——面试题65:滑动窗口的最大值 class Solution {public int[] maxSlidingWindow(int[] nums, int k) {int[] res new int[nums.length - k 1];Deque<Integer> q new LinkedList<>();int inx 0;while (inx <…...
d3dx9_43.dll丢失原因以及5个解决方法详解
在电脑使用过程中,我们可能会遇到一些错误提示,其中之一就是“d3dx9_43.dll缺失”。这个错误提示通常表示我们的电脑上缺少了DirectX的一个组件,而DirectX是游戏和多媒体应用所必需的软件。本文将介绍d3dx9_43.dll缺失对电脑的影响以及其原因…...
Python实现FA萤火虫优化算法优化卷积神经网络分类模型(CNN分类算法)项目实战
说明:这是一个机器学习实战项目(附带数据代码文档视频讲解),如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 萤火虫算法(Fire-fly algorithm,FA)由剑桥大学Yang于2009年提出 , …...
不瞒各位,不安装软件也能操作Xmind文档
大家好,我是小悟 作为搞技术的一个人群,时不时就要接收产品经理发过来的思维脑图,而此类文档往往是以Xmind编写的,如果你的电脑里面没有安装Xmind的话,不好意思,是打不开这类后缀结尾的文档。 打不开的话…...
你了解Redis 的二进制安全吗
最近面试的时候被问到Redis 的二进制安全相关八股文面试题。Redis二进制安全内容比较多,以下是简单的总结大致的过程,需要深入学习的建议跳过 Redis是基于C语言进行开发的,而C语言中的字符串是二进制不安全的,所以Redis就没有直接…...
探索前端设计的新境界——介绍IVueUI工具助力Vue页面设计
在快速发展的前端领域,Vue.js作为一款渐进式JavaScript框架,一直备受开发者喜爱。然而,在Vue前端开发的旅程中,页面设计常常是一个不可避免的挑战。今天,我要向大家介绍一款令Vue前端开发者受益匪浅的工具——www.ivue…...
数据管理系统-week10-数据库安全
文章目录 前言一、什么是数据库安全?二、威胁三、对抗措施四、授权和认证五、访问控制(重点)自由访问控制(DAC)强制访问控制(MAC)补充一个贝尔-lapadula模型六、加密参考文献前言 数据库安全意味着保护数据库免受有意或无意的未经授权的访问,数据库安全需要保护数据库…...
MySQL笔记-第05章_排序与分页
视频链接:【MySQL数据库入门到大牛,mysql安装到优化,百科全书级,全网天花板】 文章目录 第05章_排序与分页1. 排序数据1.1 排序规则1.2 单列排序1.3 多列排序 2. 分页2.1 背景2.2 实现规则2.3 拓展 第05章_排序与分页 讲师&#…...
MySQL笔记-第02章_MySQL环境搭建
视频链接:【MySQL数据库入门到大牛,mysql安装到优化,百科全书级,全网天花板】 文章目录 第02章_MySQL环境搭建1. MySQL的卸载步骤1:停止MySQL服务步骤2:软件的卸载步骤3:残余文件的清理步骤4&am…...
★136. 只出现一次的数字(位运算)
136. 只出现一次的数字 这个题主要考察的知识点是位运算(这里是异或) 如果不要求空间复杂度为O(1),那有很多方法。但是这里有这样的要求。 可以通过位运算 的方法来实现。 异或运算 ⊕有以下三个性质: 任…...
阿里云效一键部署前后端
静态站点到OSS 阿里云-云效,阿里云企业级一站式 DevOps,可以免费使用(会限制人数、流水线数量等,个人项目够用了)。相关文章 CI 持续集成 - 阿里云云效 OSS 是对象存储的意思,一般一个项目对应一个 Bucke…...
【算法集训】基础数据结构:一、顺序表(上)
顺序表是最基础的数组结构,所有数据都按顺序存储。 第一题 1464. 数组中两元素的最大乘积 https://leetcode.cn/problems/maximum-product-of-two-elements-in-an-array/description/ 第一种:常规解法,遍历两次数组根据条件比较出最大的即可…...
封装websocket并在vuejs中调用
1、创建JS文件ce-websocket-util.js class CeWebsocketUtil {websocket null;reConnectTimes 0; // 失败后重新连接次数wsInterVal null; // 重新连接定时器maxReConnectTimes 10; // 最大连接次数,默认10次reIntervalTime 60 * 1000; // 重连间隔时间,默认1m…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...
