SpringBean的生命周期
网络上对于spring生命周期的总结很多,对初学者来说看起来眼花缭乱,实际上Spring Bean的生命周期只有四个阶段。把这四个阶段和每个阶段对应的扩展点糅合在一起虽然没有问题,但是这样非常凌乱,难以记忆。要彻底搞清楚Spring的生命周期,首先要把这四个阶段牢牢记住。实例化和属性赋值对应构造方法和setter方法的注入,初始化和销毁是用户能自定义扩展的两个阶段。在这四步之间穿插的各种扩展点,稍后会讲。
- 实例化 Instantiation
- 属性赋值 Populate
- 初始化 Initialization
- 销毁 Destruction
用一张图就能贯穿我们将要学习的spring生命周期全流程
BeanDifinition定义阶段
元信息的配置和解析是一块很大的内容,后续还会专门开一篇章节分析其中的逻辑,本节只是基本介绍一下有那些类型和方案。
1.元信息配置
面向资源
有三种面向资源的bean定义信息读取方式,由于不常用,可以忽略groovy的方式。
XML方式配置
定义bean
注册bean
public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 实例化 基于properties资源的 BeanDefinitionReaderXmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);String location = "/META-INF/dependency-lookup-context.xml";// 加载 Properties 文件,用resource的方式可以避免乱码// 指定字符编码 UTF-8Resource resource = new ClassPathResource(location);EncodedResource encodedResource = new EncodedResource(resource, "UTF-8");int beanNumbers = beanDefinitionReader.loadBeanDefinitions(encodedResource);System.out.printf("已加载的 BeanDefinition 数量: %d%n", beanNumbers);// 通过 bean id 和类型进行依赖查找User user = beanFactory.getBean("user", User.class);System.out.println(user);}
Propeties配置
定义bean
注册bean定义信息
public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 实例化 基于properties资源的 BeanDefinitionReaderPropertiesBeanDefinitionReader beanDefinitionReader = new PropertiesBeanDefinitionReader(beanFactory);String location = "/META-INF/user-bean.properties";// 加载 Properties 文件,用resource的方式可以避免乱码// 指定字符编码 UTF-8Resource resource = new ClassPathResource(location);EncodedResource encodedResource = new EncodedResource(resource, "UTF-8");int beanNumbers = beanDefinitionReader.loadBeanDefinitions(encodedResource);System.out.printf("已加载的 BeanDefinition 数量: %d%n", beanNumbers);// 通过 bean id 和类型进行依赖查找User user = beanFactory.getBean("user", User.class);System.out.println(user);}
面向注解
- @component
- @import
- @bean
面向Api
命名方式
可以指定bean的名称
BeanDefinitionBuilder#registerBeanDefinition
非命名方式
不指定bean的名称,用spring默认的bean名称生成
BeanDefinitionReaderUtils.registerWithGeneratedName
配置类方式
AnnotatedBeanDefinitionReader.register()
public static void main(String[] args) {AnnotationConfigApplicationContext context =new AnnotationConfigApplicationContext(AwareConfig.class);//命名方式registerBeanDefinition(context,"user");//非命名方式registerBeanDefinition(context);//配置类方式context.register(Student.class);Map<String, Student> beansOfType = context.getBeansOfType(Student.class);System.out.println(beansOfType); } public static void registerBeanDefinition(AnnotationConfigApplicationContext context,String beanName){BeanDefinitionBuilder beanDefinitionBuilder =BeanDefinitionBuilder.genericBeanDefinition(Student.class);beanDefinitionBuilder.addPropertyValue("name","zzb").addPropertyValue("age",18);if(StringUtils.isEmpty(beanName)){//非命名方式BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinitionBuilder.getBeanDefinition(),context);}else {//命名方式context.registerBeanDefinition(beanName,beanDefinitionBuilder.getBeanDefinition());} } public static void registerBeanDefinition(AnnotationConfigApplicationContext context){registerBeanDefinition(context,null); }
2.元信息解析
针对资源的解析
有两个解析器XmlBeanDefinitionReader
和 PropertiesBeanDefinitionReader
他们都继承于AbstractBeanDefinitionReader
针对于注解的解析
有AnnotatedBeanDefinitionReader
他不继承与任何类,因为注解的扫描和资源的扫描是完全不同的
AnnotationConfigApplicationContext
上下文中默认的reader就是AnnotatedBeanDefinitionReader
3.元信息注册
我们的ioc容器支持定义信息的直接注册DefaultListableBeanFactory#registerBeanDefinition
这里注意一点,为什么有了map还需要有个names的集合,是为了保证bean注册的顺序性,这样之后就能顺序的获取bean的定义信息了。
4.元信息合并
在我们注入的类如果出现了继承的情况下,我们需要读取父类的BeanDifinition配置合并到子类来。
再什么时候会进行BeanDifinition的合并呢?
其实在BeanDifinition注册的时候,还不会进行合并,只有在第一次实例化bean的时候才会将BeanDifinition合并,这也就解释了为什么我们把合并这一小节放在元信息注册的后面。
下图所有的方法都属于AbstractBeanFactory
这里mergedBeanDefinitions
字段做了一层对bean定义的缓存,如果有就不需要再去merge一遍了。
深入下去看所有生成的定义信息,默认都是GenericBeanDefinition
如果没有parent的话,就直接包装成一个新的RootBeanDefinition
就返回了。
如果是有父节点的,就会递归去先加载父节点的定义信息,最终还是包装成RootBeanDefinition
返回。
这里会将父节点信息copy一份,然后再用子节点的信息去覆盖父节点的一些属性,比如override方法,比如一些基本属性,点进去看自行了解AbstractBeanDefinition#overrideFrom
Bean的加载阶段
我们的bean在定义的时候,他的beanClass 都是string类型的一个描述字段,那么他是什么时候转为Class类型的呢?
在创建单例bean时
可以看到这里会针对jdk规范做一些安全校验,但是我们一般都没用到,可以选择跳过
拿着AbstractBeanFactory的classloader去找到自己的类类型,并且更新自己的beanClass属性由string变成具体的class类型
Bean 实例化阶段
知道了bean的class类型,我们就可以对它进行实例化了
实例化前
实现InstantiationAwareBeanPostProcessor
接口重写postProcessBeforeInstantiation
方法,就可以截断bean的实例化,返回一个新对象
返回的不为null,就会跳过createBean步骤
实例化中
实例化的核心是该用无参实例化还是构造器实例化,如果用了构造器实例化其中传入的内容是以参数为准还是以bean类型为准去进行依赖查找。可以自行搭配源码探索
并且可以参考spring反射拿到参数名的方式。
实例化后
实现InstantiationAwareBeanPostProcessor
接口重写postProcessAfterInstantiation
方法,在bean实例化后,属性注入前,进行截断,阻止属性的注入。
属性赋值前
实现InstantiationAwareBeanPostProcessor
接口重写postProcessProperties
方法,在bean实例化后,属性注入前,可以根据属性描述,动态的新增,删除,修改一些属性。
Bean的初始化阶段
初始化中会涉及大量的后置处理器和aware接口回调
所有的初始化逻辑都会在》实例化后》属性赋值后》初始化
Aware回调
初始化前
实现BeanPostProcessor
的postProcessBeforeInstantiation
方法的bean会被会执行该方法,返回的对象会替换原有的实例化后的bean。
初始化中
bean的初始化会做以下三件事
@PostConstruct
实际上@PostConstruct 这个方法是靠CommonAnnotationBeanPostProcessor
实现的,而这个类实在bean的初始化前就进行的回调
实现InitializingBean
的afterPropertiesSet
方法
自定义初始化
有指定初始化方法的,就会回调bena内的方法
回调位置
初始化后
初始化完成
初始化完成是bean生成生命周期的最后一道关口,并且这个回调必须是显示调用了DefaultListableBeanFactory#preInstantiateSingletons的方法,对单例bean进行一个提前的初始化
Bean的销毁阶段
beanFactory.destroySingletons()当调用这个方法后,会做的一些事情
将所有容器中的单例bean相关信息移除,回调所有相关的后置处理器和bean的销毁方法。
销毁前
销毁中
销毁中有三个回调
@PreDestroy其实也是包装在CommonAnnotationBeanPostProcessor
中回调的bean销毁的后置处理器,先后顺序就看后置处理器注册时候的顺序了
先后顺序,可以从图中看出
DisposableBean
指定的销毁
Bean的垃圾回收
关闭spring容器并不会立刻进行垃圾回收,需要等待系统GC,我们也可以手动调用系统的GC。
为了验证是否真的被gc,我们还可以让ben重写finalize()方法,看看对应的情况。
总结
Spring Bean的生命周期分为四个阶段和多个扩展点。扩展点又可以分为影响多个Bean和影响单个Bean。整理如下:
四个阶段
- 实例化 Instantiation
- 属性赋值 Populate
- 初始化 Initialization
- 销毁 Destruction
多个扩展点
- 影响多个Bean
- BeanPostProcessor
- InstantiationAwareBeanPostProcessor
- 影响单个Bean
- Aware
- Aware Group1
- BeanNameAware
- BeanClassLoaderAware
- BeanFactoryAware
- Aware Group2
- EnvironmentAware
- EmbeddedValueResolverAware
- ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware)
- 生命周期
- InitializingBean
- DisposableBean
参考
参考博客
SpringBean的生命周期相关推荐
- 一步步实现:springbean的生命周期测试代码
1. 创建实体SpringBean public class SpringBean {private String username;public String getUsername() {retu ...
- 记一次李姐SpringBean的生命周期
SpringIOC的加载过程 1.首先通过BeanDefinitionReader读取配置文件,得到完成的Bean定义对象BeanDefinition对象,此时只是得到Bean的相关定义信息,还没有开 ...
- 【Spring注解系列10】SpringBean的生命周期
1.SpringBean生命周期定义 指bean创建---初始化----销毁的过程. 构造(对象创建): 单实例:在容器启动的时候创建对象 多实例:在每次获取的时候创建对象 初始化和销毁方式: 指定初 ...
- springBean的生命周期 面试好记无废话
springBean生命周期 实例化阶段 1.spring读取xml配置文件,容器就会调用doCreateBean方法进行实例化,底层是通过工厂+反射完成的创建 2.bean实例化后,进行bean对象 ...
- Spring-bean的生命周期
1.生命周期:从对象创建到对象销毁的过程 2.bean的生命周期 (1)通过构造器创建bean实例(无参构造) (2)为bean的属性设置值和对其他bean的引用(调用set方法) (3)调用bean ...
- spring----Bean的生命周期和循环依赖
循环依赖: A类引用了B,B类引用了A,像这种循环着依赖就是循环依赖: 对于这种配置不会报错 <bean id="instanceA" class="com.zy. ...
- springbean的生命周期_spring bean生命周期(涵盖spring常用接口的载入)
spring bean生命周期流程图: 其中包含了很多常用的接口,可以参考spring 常用接口: 下面写个例子证明下: 1.实现InitializingBean以及各个Aware接口 p ...
- SpringBean生命周期的理解
文章目录 前言 一.BeanFactory 和 ApplicationContext 1.1什么是Spring bean? 1.2BeanFactory 1.3ApplicationContext 1 ...
- spring生命周期七个过程_Spring杂文(三)Spring循环引用
众所周知spring在默认单例的情况下是支持循环引用的 Appconfig.java类的代码 @Configurable @ComponentScan("com.sadow") p ...
- Spring bean生命周期概览
springBean的生命周期描述了交由spring管理的bean从创建,属性赋值,代理扩展,销毁的过程.在不同的阶段spring都提供了相应的接口提供扩展. Spring生命周期 各个接口与方法调用 ...
最新文章
- Yii2多模型与事务的用法
- 走进腾讯 |《产品经理第一课》宜信、零度无人机、悦跑圈核心团队独家分享爆款产品的打造秘籍...
- django的orm指定字段名,表名 verbose_name_plural
- python中列表相加规则_在Python字典列表中使用公共键/值求和值
- BScroll 实时监听滚动位置
- 博通无线网卡驱动安装linux,Ubuntu下Broadcom 802.11g无线网卡驱动安装方法
- 解决beyond compare秘钥被吊销的问题
- 【Numpy】1. n维数组,dtype,切片,索引
- ubuntu18.04未发现wifi适配器,安装wifi无线网卡驱动-RTL8822BE、RTL8822CE、RTL8821CE、RTL8723DE
- 字符转ASII码以及大小写之间的转换
- python中添加.pth_使用.pth文件扩展python环境路径
- ABB机器人画圆编程_ABB机器人使用rapid编程中问号的用法及画整圆指令
- 肾功能及早期肾损伤的检查题库【1】
- Apache httpd服务器下载及安装
- 虚拟独享服务器,独享云虚拟主机和服务器
- 背了单词再也不怕忘,多邻国的AI要逆天!
- 什么是大数据,大数据的处理流程介绍
- 轨道看盘系统 通达信选股公式 看盘指标详解主图/副图
- 如何在大型系统中提供拼音检索服务
- NETCTOSS代码实现第八版