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.hqwc.cn/news/247099.html

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

相关文章

affinity photo和ps区别Affinity VS Ps 那个更亲民

在图像处理和编辑领域&#xff0c;很多人经常比较Affinity Photo和Adobe Photoshop&#xff08;PS&#xff09;这两款软件。它们都是功能强大的图像处理工具&#xff0c;但在某些方面存在明显的区别。了解affinity photo和ps的区别以及affinity photo的价格有助于选择适合自己需…

从0开始使用Maven

文章目录 一.Maven的介绍即相关概念1.为什么使用Maven/Maven的作用2.Maven的坐标 二.Maven的安装三.IDEA编译器配置Maven环境1.在IDEA的单个工程中配置Maven环境2.方式2&#xff1a;配置Maven全局参数 四.IDEA编译器创建Maven项目五.IDEA中的Maven项目结构六.IDEA编译器导入Mav…

电子书制作神器!错过等十年

众所周知&#xff0c;随着科技的飞速发展&#xff0c;电子书已成为越来越多人的首选阅读方式。但制作电子书并不费力&#xff0c;一个制作电子书的神器就能解决这些问题。 那这款神器究竟有何魅力&#xff1f;它能帮助我们制作出怎样的电子书&#xff1f; 首先&#xff0c;这款…

Apache Sqoop使用

1. Sqoop介绍 Apache Sqoop 是在 Hadoop 生态体系和 RDBMS 体系之间传送数据的一种工具。 Sqoop 工作机制是将导入或导出命令翻译成 mapreduce 程序来实现。在翻译出的 mapreduce 中主要是对 inputformat 和 outputformat 进行定制。 Hadoop 生态系统包括&#xff1a;HDFS、Hi…

龙迅分配器LT86102UXE/LT86104UX,HDMI一分二/HDMI一分四

龙迅LT86102UXE描述; Lontium LT86102UXE HDMI2.0分配器具有1&#xff1a;2的分配器&#xff0c;符合HDMI2.0/1.4规范&#xff0c;最大6Gbps高速数据速率&#xff0c;自适应均衡RX输入和预先强调的TX输出&#xff0c;以支持长电缆应用程序&#xff0c;内部TX通道交换灵活的PCB…

阿里云效一键部署前后端

静态站点到OSS 阿里云-云效&#xff0c;阿里云企业级一站式 DevOps&#xff0c;可以免费使用&#xff08;会限制人数、流水线数量等&#xff0c;个人项目够用了&#xff09;。相关文章 CI 持续集成 - 阿里云云效 OSS 是对象存储的意思&#xff0c;一般一个项目对应一个 Bucke…

(c语言进阶)结构体内存对齐和修改默认对齐数

一.结构体内存对齐 结构体内存大小计算方法&#xff1a; 偏移量&#xff1a;是指某个成员在结构体中相对于结构体首地址的偏移字节数。在计算机中&#xff0c;结构体是一种自定义数据类型&#xff0c;它由多个不同类型的成员组成。每个成员在内存中的存储位置是连续的&#xf…

【c】有序数列插入一个整数

#include<stdio.h> int main() {int n;scanf("%d",&n);int arr[n1];for(int i0;i<n;i){scanf("%d",&arr[i]);}int a;scanf("%d",&a);arr[n]a;for(int j0;j<n;j){if(arr[j]>arr[n])//交换元素位置{int temparr[j];arr…

2023.12.3 关于 Spring 拦截器 和 过滤器

目录 引言 Spring 拦截器实现 实例理解 Spring 过滤器实现 实例理解 拦截器和过滤器的区别 出身不同 触发时机不同 底层实现不同 支持的项目类型不同 使用场景不同 引言 原生 Spring AOP 实现统一拦截有两个难点难点一&#xff1a;定义拦截规则表达式 难点二&#…

CefSharp 获取POST(AJAX)、GET消息返回值(request)

CefSharp作为专门为爬虫工具开发的库比Selenium这种开发目的是页面测试工具然后用来做爬虫的工具要贴心得多。我们操作网页的时候发送或者做了某个动作提交表单之后需要知道我们的动作或者提交是否成功&#xff0c;因为有的页面会因为网络延迟问题提交失败&#xff0c;需要准确…

VSCode 开发C/C++实用插件分享——koroFileHeader

相关文章 VSCode 开发C/C实用插件分享——codegeex VSCode 开发C/C实用插件分享——koroFileHeader 一、koroFileHeader二、使用步骤1.安装2.头文件注释配置3.函数注释配置 一、koroFileHeader 在有些场景下&#xff0c;我们需要在文件头添加一些作者、文件描述、时间和版权描述…

12月第一天,给搞电商的家人们整活儿!

今年&#xff0c;大家出门的热情直线飙升&#xff01; 特种兵旅游带火N个小众城市、Citywalk大军激活大街小巷的商业活力、线下演出火爆带动各地文旅指数狂飙&#xff0c;户外运动也乘风而上&#xff0c;徒步/露营/钓鱼/骑行/冲浪/滑雪等花式运动在各大社媒平台集中刷屏&#…