Spring Bean的生命周期是Spring面试热点问题。这个问题即考察对Spring的微观了解,又考察对Spring的宏观认识,想要答好并不容易!本文希望能够从源码角度入手,帮助面试者彻底搞定Spring Bean的生命周期。

文章目录

  • 简述bean的生命周期
  • 常用扩展点
    • 第一大类:影响多个Bean的接口
    • 第二大类:只调用一次的接口
      • 无所不知的Aware
      • Aware调用时机源码分析
      • 简单的两个生命周期接口
    • 扩展阅读:BeanPostProcessor注册时机与执行顺序
      • 注册时机
      • 执行顺序
  • 本文小结

简述bean的生命周期

实例化 -> 填充属性 -> 初始化 -> 销毁

SpringBean生命周期简单概括为4个阶段:

1.实例化,创建一个Bean对象

2.填充属性,为属性赋值

3.初始化

  • 如果实现了xxxAware接口,通过不同类型的Aware接口拿到Spring容器的资源
  • 如果实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation和postProcessAfterInitialization方法
  • 如果配置了init-method方法,则会执行init-method配置的方法

4.销毁

  • 容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy方法
  • 如果配置了destroy-method方法,则会执行destroy-method配置的方法


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

主要逻辑都在doCreateBean()方法中,逻辑很清晰,就是顺序调用以下三个方法,这三个方法与三个生命周期阶段一一对应,非常重要,在后续扩展接口分析中也会涉及。

  1. createBeanInstance() -> 实例化
  2. populateBean() -> 属性赋值
  3. initializeBean() -> 初始化

源码如下,能证明实例化,属性赋值和初始化这三个生命周期的存在。关于本文的Spring源码都将忽略无关部分,便于理解:

// 忽略了无关代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;if (instanceWrapper == null) {// 实例化阶段!instanceWrapper = createBeanInstance(beanName, mbd, args);}// Initialize the bean instance.Object exposedObject = bean;try {// 属性赋值阶段!populateBean(beanName, mbd, instanceWrapper);// 初始化阶段!exposedObject = initializeBean(beanName, exposedObject, mbd);}}

至于销毁,是在容器关闭时调用的,详见ConfigurableApplicationContext#close()


常用扩展点

Spring生命周期相关的常用扩展点非常多,所以问题不是不知道,而是记不住或者记不牢。其实记不住的根本原因还是不够了解,这里通过源码+分类的方式帮大家记忆。

第一大类:影响多个Bean的接口

实现了这些接口的Bean会切入到多个Bean的生命周期中。正因为如此,这些接口的功能非常强大,Spring内部扩展也经常使用这些接口,例如自动注入以及AOP的实现都和他们有关。

  • BeanPostProcessor
  • InstantiationAwareBeanPostProcessor

这两兄弟可能是Spring扩展中最重要的两个接口!InstantiationAwareBeanPostProcessor作用于实例化阶段的前后,BeanPostProcessor作用于初始化阶段的前后。正好和第一、第三个生命周期阶段对应。通过图能更好理解:


InstantiationAwareBeanPostProcessor实际上继承了BeanPostProcessor接口,严格意义上来看他们不是两兄弟,而是两父子。但是从生命周期角度我们重点关注其特有的对实例化阶段的影响,图中省略了从BeanPostProcessor继承的方法。

InstantiationAwareBeanPostProcessor extends BeanPostProcessor

InstantiationAwareBeanPostProcessor源码分析

  • postProcessBeforeInstantiation调用点,忽略无关代码:
    @Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {try {// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.// postProcessBeforeInstantiation方法调用点,这里就不跟进了,// 有兴趣的同学可以自己看下,就是for循环调用所有的InstantiationAwareBeanPostProcessorObject bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean;}}try {   // 上文提到的doCreateBean方法,可以看到// postProcessBeforeInstantiation方法在创建Bean之前调用Object beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isTraceEnabled()) {logger.trace("Finished creating instance of bean '" + beanName + "'");}return beanInstance;}}

可以看到,postProcessBeforeInstantiation在doCreateBean之前调用,也就是在bean实例化之前调用的,英文源码注释解释道该方法的返回值会替换原本的Bean作为代理,这也是Aop等功能实现的关键点。

  • postProcessAfterInstantiation调用点,忽略无关代码:
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the// state of the bean before properties are set. This can be used, for example,// to support styles of field injection.boolean continueWithPropertyPopulation = true;// InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()// 方法作为属性赋值的前置检查条件,在属性赋值之前执行,能够影响是否进行属性赋值!if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {continueWithPropertyPopulation = false;break;}}}}// 忽略后续的属性赋值操作代码
}

可以看到该方法在属性赋值方法内,但是在真正执行赋值操作之前。其返回值为boolean,返回false时可以阻断属性赋值阶段(continueWithPropertyPopulation = false;)。

关于BeanPostProcessor执行阶段的源码穿插在下文Aware接口的调用时机分析中,因为部分Aware功能的就是通过他实现的!只需要先记住BeanPostProcessor在初始化前后调用就可以了


第二大类:只调用一次的接口

这一大类接口的特点是功能丰富,常用于用户自定义扩展。

第二大类中又可以分为两类

  • Aware类型的接口
  • 生命周期接口

无所不知的Aware

Aware类型的接口的作用就是让我们能够拿到Spring容器中的一些资源。基本都能够见名知意,Aware之前的名字就是可以拿到什么资源,例如BeanNameAware可以拿到BeanName,以此类推。调用时机需要注意:所有的Aware方法都是在初始化阶段之前调用的!

Aware接口众多,这里同样通过分类的方式帮助大家记忆。

Aware接口具体可以分为两组,至于为什么这么分,详见下面的源码分析。如下排列顺序同样也是Aware接口的执行顺序,能够见名知意的接口不再解释。

Aware Group1

  1. BeanNameAware
  2. BeanClassLoaderAware
  3. BeanFactoryAware

Aware Group2

  1. EnvironmentAware
  2. EmbeddedValueResolverAware 这个知道的人可能不多,实现该接口能够获取Spring EL解析器,用户的自定义注解需要支持spel表达式的时候可以使用,非常方便。
  3. ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware) 这几个接口可能让人有点懵,实际上这几个接口可以一起记,其返回值实质上都是当前的ApplicationContext对象,因为ApplicationContext是一个复合接口,如下:
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}

这里涉及到另一道面试题,ApplicationContext和BeanFactory的区别,可以从ApplicationContext继承的这几个接口入手,除去BeanFactory相关的两个接口就是ApplicationContext独有的功能,这里不详细说明


Aware调用时机源码分析

详情如下,忽略了部分无关代码。代码位置就是我们上文提到的initializeBean方法详情,这也说明了Aware都是在初始化阶段之前调用的!

    // 见名知意,初始化阶段调用的方法protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {// 这里调用的是Group1中的三个Bean开头的AwareinvokeAwareMethods(beanName, bean);Object wrappedBean = bean;// 这里调用的是Group2中的几个Aware,// 而实质上这里就是前面所说的BeanPostProcessor的调用点!// 也就是说与Group1中的Aware不同,这里是通过BeanPostProcessor(ApplicationContextAwareProcessor)实现的。wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);// 下文即将介绍的InitializingBean调用点invokeInitMethods(beanName, wrappedBean, mbd);// BeanPostProcessor的另一个调用点wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);return wrappedBean;}

可以看到并不是所有的Aware接口都使用同样的方式调用。Bean××Aware都是在代码中直接调用的,而ApplicationContext相关的Aware都是通过BeanPostProcessor#postProcessBeforeInitialization()实现的。感兴趣的可以自己看一下ApplicationContextAwareProcessor这个类的源码,就是判断当前创建的Bean是否实现了相关的Aware方法,如果实现了会调用回调方法将资源传递给Bean。

至于Spring为什么这么实现,应该没什么特殊的考量。也许和Spring的版本升级有关。基于对修改关闭,对扩展开放的原则,Spring对一些新的Aware采用了扩展的方式添加。

BeanPostProcessor的调用时机也能在这里体现,包围住invokeInitMethods方法,也就说明了在初始化阶段的前后执行。

关于Aware接口的执行顺序,其实只需要记住第一组在第二组执行之前就行了。每组中各个Aware方法的调用顺序其实没有必要记,有需要的时候点进源码一看便知。


简单的两个生命周期接口

至于剩下的两个生命周期接口就很简单了,实例化和属性赋值都是Spring帮助我们做的,能够自己实现的有初始化和销毁两个生命周期阶段。

  1. InitializingBean 对应生命周期的初始化阶段,在上面源码的invokeInitMethods(beanName, wrappedBean, mbd);方法中调用。
    有一点需要注意,因为Aware方法都是执行在初始化方法之前,所以可以在初始化方法中放心大胆的使用Aware接口获取的资源,这也是我们自定义扩展Spring的常用方式。
    除了实现InitializingBean接口之外还能通过注解或者xml配置的方式指定初始化方法,至于这几种定义方式的调用顺序其实没有必要记。因为这几个方法对应的都是同一个生命周期,只是实现方式不同,我们一般只采用其中一种方式。

  2. DisposableBean 类似于InitializingBean,对应生命周期的销毁阶段,以ConfigurableApplicationContext#close()方法作为入口,实现是通过循环取所有实现了DisposableBean接口的Bean然后调用其destroy()方法 。感兴趣的可以自行跟一下源码。


扩展阅读:BeanPostProcessor注册时机与执行顺序

注册时机

我们知道BeanPostProcessor也会注册为Bean,那么Spring是如何保证BeanPostProcessor在我们的业务Bean之前初始化完成呢?

请看我们熟悉的refresh()方法的源码,省略部分无关代码:

    @Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.// 所有BeanPostProcesser初始化的调用点registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.// 所有单例非懒加载Bean的调用点finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}}

可以看出,Spring是先执行registerBeanPostProcessors()进行BeanPostProcessors的注册,然后再执行finishBeanFactoryInitialization初始化我们的单例非懒加载的Bean。


执行顺序

BeanPostProcessor有很多个,而且每个BeanPostProcessor都影响多个Bean,其执行顺序至关重要,必须能够控制其执行顺序才行。关于执行顺序这里需要引入两个排序相关的接口:PriorityOrdered、Ordered

  1. PriorityOrdered是一等公民,首先被执行,PriorityOrdered公民之间通过接口返回值排序
  2. Ordered是二等公民,然后执行,Ordered公民之间通过接口返回值排序
  3. 都没有实现是三等公民,最后执行

在以下源码中,可以很清晰的看到Spring注册各种类型BeanPostProcessor的逻辑,根据实现不同排序接口进行分组。优先级高的先加入,优先级低的后加入。

            // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.// 首先,加入实现了PriorityOrdered接口的BeanPostProcessors,顺便根据PriorityOrdered排了序String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);currentRegistryProcessors.clear();// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.// 然后,加入实现了Ordered接口的BeanPostProcessors,顺便根据Ordered排了序postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);currentRegistryProcessors.clear();// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.// 最后加入其他常规的BeanPostProcessorsboolean reiterate = true;while (reiterate) {reiterate = false;postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {if (!processedBeans.contains(ppName)) {currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);reiterate = true;}}sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);currentRegistryProcessors.clear();}

根据排序接口返回值排序,默认升序排序,返回值越低优先级越高。

   /*** Useful constant for the highest precedence value.* @see java.lang.Integer#MIN_VALUE*/int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;/*** Useful constant for the lowest precedence value.* @see java.lang.Integer#MAX_VALUE*/int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

PriorityOrdered、Ordered接口作为Spring整个框架通用的排序接口,在Spring中应用广泛,也是非常重要的接口。


本文小结

Spring Bean的生命周期分为四个阶段和多个扩展点。扩展点又可以分为影响多个Bean和影响单个Bean。整理如下:

四个阶段

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

多个扩展点

  1. 影响多个Bean
  2. 影响单个Bean

至此,Spring Bean的生命周期介绍完毕。

再聊Spring Bean的生命周期相关推荐

  1. 带你读懂Spring Bean 的生命周期,嘿,就是玩儿~

    带你读懂Spring Bean 的生命周期,嘿,就是玩儿~ 一.前言 今天我们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象.因为 Spring Bean 的 ...

  2. 字节跳动面试题:“请你描述下 Spring Bean 的生命周期?”

    1. 引言 "请你描述下 Spring Bean 的生命周期?",这是面试官考察 Spring 的常用问题,可见是 Spring 中很重要的知识点. 我之前在准备面试时,去网上搜过 ...

  3. 简述 Spring Bean的生命周期

    "请你描述下 Spring Bean 的生命周期?",这是面试官考察 Spring 的常用问题,可见是 Spring 中很重要的知识点. 其实要记忆该过程,还是需要我们先去理解,本 ...

  4. Spring bean 实现生命周期的三种解决方案

    Spring bean 实现生命周期的三种解决方案 参考文章: (1)Spring bean 实现生命周期的三种解决方案 (2)https://www.cnblogs.com/javawebsoa/a ...

  5. 请解释Spring Bean 的生命周期?

    Spring Bean 的生命周期简单易懂.在一个bean 实例被初始化时,需要执行一系列的初始化操作以达到可用的状态.同样的,当一个bean 不在被调用时需要进行相关的析构操作,并从bean 容器中 ...

  6. Spring Bean的生命周期及接口回调

    本篇介绍Spring框架为Spring Bean生命周期各阶段提供的回调接口,程序通过实现回调接口,可以在IOC容器实例化或销毁Bean的过程中,得到Bean的控制权,并对Bean进行预处理工作.通过 ...

  7. Spring Bean的生命周期(非常详细)

    Spring作为当前Java最流行.最强大的轻量级框架,受到了程序员的热烈欢迎.准确的了解Spring Bean的生命周期是非常必要的.我们通常使用ApplicationContext作为Spring ...

  8. 【Spring Bean的生命周期】

    Spring Bean的生命周期(非常详细) - Chandler Qian - 博客园

  9. Spring Bean的生命周期(二)

    上一篇文章大致说明了Spring Bean的生命周期,本章详细说明各个阶段调用的接口方法. 1.1 - 3.2 对应 实例化 Instantiation 4.1 - 4.2 对应 属性赋值 Popul ...

最新文章

  1. spring装载配置文件失败报错:org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException...
  2. 分子生物学-共价键基础
  3. Spring BeanFactoryPostProcessor接口详细使用
  4. 【leetcode】Search for a Range
  5. 前台为html后台是asp在vs2012中建立什么,Asp.net中后台*.cs与前台JS脚本之间相互调用的几点心得...
  6. tiny core linux ftp,tinycore 的基本搭建,开机时间只需要1-3秒
  7. 把dataset作为一个xml文件传给客户端
  8. C语言基础-01-指针
  9. 用vbs脚本实现软件的自动登录
  10. 学习笔记(02):Qt视频教程-基础实例进阶-网页爬虫-图片抓取
  11. 如何将mp4视频转换成flv格式
  12. 如何html设置下载的字体呢?
  13. 解读2017年云计算发展趋势 — 简本
  14. uC/Modbus 用户手册——第一节
  15. Kubernetes(k8s)的Secret以密文的方式存储数据
  16. python好学吗一般要学多久-python好学吗
  17. 改进YOLOv5 | Stand-Alone Self-Attention | 针对视觉任务的独立自注意力层 | 搭建纯注意力FPN+PAN结构
  18. C语言中 srand()函数和rand()函数
  19. 【U8】登录账套显示“账套XXX年度XXXX是以前版本的数据,请使用系统管理升级”
  20. vscode使用问题合集

热门文章

  1. kickstart 为 rhel5 创建 ext4 分区
  2. 如何在eclipse中使用XYLayout布局?在此介绍如何把XYLayout导入到eclipse .
  3. she's gone
  4. 试图登录,但是网络登陆服务没有启动成功
  5. indexOf 方法
  6. QTP的那些事--XPath的重要使用
  7. Spark Streaming实时数据分析
  8. Windows环境下JDK安装与环境变量配置详细的图文教程
  9. 96.2. Yum 安装
  10. Eclipse StartExplorer插件