news 2026/4/18 3:46:36

SpringBean的生命周期

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBean的生命周期

网络上对于spring生命周期的总结很多,对初学者来说看起来眼花缭乱,实际上Spring Bean的生命周期只有四个阶段。把这四个阶段和每个阶段对应的扩展点糅合在一起虽然没有问题,但是这样非常凌乱,难以记忆。要彻底搞清楚Spring的生命周期,首先要把这四个阶段牢牢记住。实例化和属性赋值对应构造方法和setter方法的注入,初始化和销毁是用户能自定义扩展的两个阶段。在这四步之间穿插的各种扩展点,稍后会讲。

  1. 实例化 Instantiation
  2. 属性赋值 Populate
  3. 初始化 Initialization
  4. 销毁 Destruction

用一张图就能贯穿我们将要学习的spring生命周期全流程

BeanDifinition定义阶段

元信息的配置和解析是一块很大的内容,后续还会专门开一篇章节分析其中的逻辑,本节只是基本介绍一下有那些类型和方案。

1.元信息配置

面向资源

有三种面向资源的bean定义信息读取方式,由于不常用,可以忽略groovy的方式。

XML方式配置

定义bean

注册bean

public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 实例化 基于properties资源的 BeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
String location = “/META-INF/dependency-lookup-context.xml”;
// 加载 Properties 文件,用resource的方式可以避免乱码
// 指定字符编码 UTF-8
Resource 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资源的 BeanDefinitionReader
PropertiesBeanDefinitionReader beanDefinitionReader = new PropertiesBeanDefinitionReader(beanFactory);
String location = “/META-INF/user-bean.properties”;
// 加载 Properties 文件,用resource的方式可以避免乱码
// 指定字符编码 UTF-8
Resource 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.元信息解析

针对资源的解析

有两个解析器XmlBeanDefinitionReaderPropertiesBeanDefinitionReader他们都继承于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回调

初始化前

实现BeanPostProcessorpostProcessBeforeInstantiation方法的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(ResourceLoaderAwareApplicationEventPublisherAwareMessageSourceAware)
    • 生命周期
      • InitializingBean
      • DisposableBean

参考

参考博客

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/27 13:19:11

从商业API到自建:HY-MT1.5翻译系统迁移指南

从商业API到自建&#xff1a;HY-MT1.5翻译系统迁移指南 在当前全球化业务快速发展的背景下&#xff0c;高质量、低延迟的翻译能力已成为众多企业不可或缺的技术基础设施。长期以来&#xff0c;开发者依赖 Google Translate、DeepL 等商业 API 提供翻译服务&#xff0c;虽然集成…

作者头像 李华
网站建设 2026/3/28 2:45:01

HY-MT1.5-7B模型压缩:8bit量化实践

HY-MT1.5-7B模型压缩&#xff1a;8bit量化实践 随着大模型在翻译任务中的广泛应用&#xff0c;如何在保证翻译质量的同时降低部署成本、提升推理效率&#xff0c;成为工程落地的关键挑战。腾讯开源的混元翻译大模型HY-MT1.5系列&#xff0c;包含HY-MT1.5-1.8B和HY-MT1.5-7B两个…

作者头像 李华
网站建设 2026/4/9 15:49:37

考虑蒙特卡洛模拟下基于闵可夫斯基和的电动汽车集群可调度功率预测模型 程序包括24时段与96时段两种情形的仿真 求解环境,Matlab和cplex求解器

考虑蒙特卡洛模拟下基于闵可夫斯基和的电动汽车集群可调度功率预测模型 程序包括24时段与96时段两种情形的仿真 求解环境&#xff0c;Matlab和cplex求解器 假设有 2 个电动汽车集群&#xff1a;分为夜间并网型和白天并网型&#xff0c;对2个集群内的电动汽车聚合计算总功率域。…

作者头像 李华
网站建设 2026/4/16 17:55:19

基于STM32的rs485modbus协议源代码实现完整示例

基于STM32的RS485 Modbus通信实战&#xff1a;从硬件连接到代码落地在工业现场&#xff0c;你是否曾为多个传感器与控制器之间的布线复杂、通信不稳定而头疼&#xff1f;是否遇到过不同厂家设备因协议不兼容&#xff0c;导致系统集成困难&#xff1f;今天&#xff0c;我们来解决…

作者头像 李华
网站建设 2026/4/16 16:42:56

Hunyuan HY-MT1.5多语言支持:33语种切换部署实操

Hunyuan HY-MT1.5多语言支持&#xff1a;33语种切换部署实操 随着全球化进程加速&#xff0c;高质量、低延迟的多语言翻译需求日益增长。传统云翻译服务虽性能稳定&#xff0c;但在隐私保护、实时性和边缘场景中面临挑战。腾讯开源的混元翻译大模型 HY-MT1.5 系列应运而生&…

作者头像 李华
网站建设 2026/3/29 1:53:08

腾讯开源HY-MT1.5:模型量化压缩技术解析

腾讯开源HY-MT1.5&#xff1a;模型量化压缩技术解析 1. 技术背景与问题提出 近年来&#xff0c;随着大语言模型在自然语言处理任务中的广泛应用&#xff0c;翻译模型的性能不断提升。然而&#xff0c;高精度往往伴随着巨大的参数量和计算开销&#xff0c;导致模型难以在资源受…

作者头像 李华