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

一、前言
今天我们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象。因为 Spring Bean 的生命周期是除了 IoC、AOP 几个核心概念之外最重要概念,大家务必拿下。可 Spring 源代码又比较复杂,跟着跟着就不知道跟到哪里去了,不太好拿下呀。这倒是真的,而且网上一上来就各种贴流程源码,对初学者来说是真的一脸懵逼,就像字都看的懂,但连在一块就不知道意思了,太绕了。

本文Remi酱试着讲的通俗易懂些,让更多的小伙伴们轻松的读懂 Spring Bean 的生命周期,并有对它有继续研究学习的想法,那我写此文的目的也就达到了。

我们讲 Spring Bean 的生命周期之前先来了解两个概念:
1.1 什么是 Bean
我们来看下 Spring Framework 的官方文档:

In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.

简而言之,bean 是由 Spring IoC 容器实例化、组装和管理的对象。
1.2 什么是 Spring Bean 的生命周期
对于普通的 Java 对象,当 new 的时候创建对象,然后该对象就能够使用了。一旦该对象不再被使用,则由 Java 自动进行垃圾回收。

而 Spring 中的对象是 bean,bean 和普通的 Java 对象没啥大的区别,只不过 Spring 不再自己去 new 对象了,而是由 IoC 容器去帮助我们实例化对象并且管理它,我们需要哪个对象,去问 IoC 容器要即可。IoC 其实就是解决对象之间的耦合问题,Spring Bean 的生命周期完全由容器控制。

二、Spring Bean 的生命周期

这里我们说的 Spring Bean 的生命周期主要指的是 singleton bean,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。

我们也来复习下 Spring 中的 bean 的作用域有哪些?

singleton
prototype
request
session
global-session

我们知道对于普通的 Java 对象来说,它们的生命周期就是:

实例化
该对象不再被使用时通过垃圾回收机制进行回收
而对于 Spring Bean 的生命周期来说:

实例化 Instantiation
属性赋值 Populate
初始化 Initialization
销毁 Destruction
实例化 -> 属性赋值 -> 初始化 -> 销毁

只有四个步骤,这样拆解的话是不是感觉也不难?不像其他人写的那样直接一上来就各种 BeanPostProcessor、BeanFactoryPostProcessor 全部怼进流程里去,别说读者看着头大,自己写的可能短时间内还记得流程,隔个一段时间,你可能都不知道自己写了个啥。

本来想通过 Bean 创建流程入口

AbstractApplicationContext#refresh() 方法的 finishBeanFactoryInitialization(beanFactory) 处带大家跟一下源码,想了想还是不带入过多的代码进来,直接给到最终的主要逻辑。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {// 实例化阶段instanceWrapper = this.createBeanInstance(beanName, mbd, args);}...Object exposedObject = bean;try {// 属性赋值阶段this.populateBean(beanName, mbd, instanceWrapper);// 初始化阶段exposedObject = this.initializeBean(beanName, exposedObject, mbd);} catch (Throwable var18) {...}...
}

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

是不是很清爽了?至于 BeanPostProcessor、BeanFactoryPostProcessor 以及其他的类,在老周看来,只不过是对主流程四个步骤的一系列扩展点而已。

三、Spring Bean 的生命周期的扩展点

Spring Bean 的生命周期的扩展点超级多,老周这里不可能全部列出来,只说核心的扩展点。这也就是为什么 Spring 的扩展性很好的原因,开了很多的口子,尽可能让某个功能高内聚松耦合,用户需要哪个功能就用哪个,而不是直接来一个大而全的东西。

3.1 Bean 自身的方法
比如构造函数、getter/setter 以及 init-method 和 destory-method 所指定的方法等,也就对应着上文说的实例化 -> 属性赋值 -> 初始化 -> 销毁四个阶段。

3.2 容器级的方法(BeanPostProcessor 一系列接口)

主要是后处理器方法,比如下图的 InstantiationAwareBeanPostProcessor 、 BeanPostProcessor 接口方法。这些接口的实现类是独立于 Bean 的,并且会注册到 Spring 容器中。在 Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用。

3.2.1 InstantiationAwareBeanPostProcessor 源码分析

我们翻一下源码发现 InstantiationAwareBeanPostProcessor 是继承了 BeanPostProcessor

InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 调用点
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)

返回值:如果返回的不为null,那么后续的Bean的创建流程【实例化、初始化afterProperties】都不会执行,而是直接使用返回的快捷Bean,此时的正常执行顺序如下:

InstantiationAwareBeanPostProcessor接口中的postProcessBeforeInstantiation,在实例化之前调用。

BeanPostProcessor接口中的postProcessAfterInitialization,在实例化之后调用。




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

InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation 调用点
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException

正常情况下在实例化之后在执行populateBean之前调用

返回值:如果有指定的bean的时候返回false,那么后续的属性填充和属性依赖注入【populateBean】将不会执行,同时后续的postProcessPropertyValues将不会执行,但是初始化和BeanPostProcessor的仍然会执行。


public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)

实例化之后调用,在方法applyPropertyValues【属性填充】之前

返回值:如果返回null,那么将不会进行后续的属性填充,比如依赖注入等,如果返回的pvs额外的添加了属性,那么后续会填充到该类对应的属性中。

pvs:PropertyValues对象,用于封装指定类的对象,简单来说就是PropertyValue的集合,里面相当于以key-value形式存放类的属性和值。

pds:PropertyDescriptor对象数组,PropertyDescriptor相当于存储类的属性,不过可以调用set,get方法设置和获取对应属性的值。

3.2.2 BeanPostProcessor 源码分析

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean


进入初始化接口:

我们先来看

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization

processor.postProcessBeforeInitialization(result, beanName);
org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization


进入 invokeAwareInterfaces(bean); 方法,当前 bean 实现了 ApplicationContextAware 接口。


ApplicationContextAwareProcessor#postProcessBeforeInitialization 首先判断此 bean 是不是各种的Aware,如果是它列举的那几个 Aware 就获取 Bean 工厂的权限,可以向容器中导入相关的上下文环境,目的是为了 Bean 实例能够获取到相关的上下文,如果不是它列举的几个 Aware,那就调用 invokeAwareInterfaces(bean) ,向容器中添加相关接口的上下文环境。
3.3 工厂后处理器方法(BeanFactoryProcessor 一系列接口)
包括 AspectJWeavingEnabler 、 CustomAutowireConfigurer 、 ConfigurationClassPostProcessor 等。这些都是 Spring 框架中已经实现好的 BeanFactoryPostProcessor,用来实现某些特定的功能。

我们知道 Spring IoC 容器初始化的关键环节就在 org.springframework.context.support.AbstractApplicationContext#refresh 方法中 ,容器创建的主体流程都在这个方法里面,这个方法是真的重要!!!

对于工厂后处理器方法老周这里直接带你看 invokeBeanFactoryPostProcessors(beanFactory); 方法,这个方法处理的是 BeanFactoryPostProcessor 接口的 Bean。调用方法如下:

跟到最重要的方法里去,代码虽长,但逻辑中规中矩。

BeanFactoryPostProcessor :一切处理 BeanFactory 的父接口

BeanDefinitionRegistryPostProcessor :实现了 BeanFactoryPostProcessor 接口的接口

流程说明:

调用 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry(registry) 方法。参数 beanFactoryPostProcessors 传入的优先处理掉。然后获取容器注册的,对于这些 Bean 按照 PriorityOrdered 接口、Ordered、没有排序接口的实例分别进行处理。
调用 BeanFactoryPostProcessor#postProcessBeanFactory(beanFactory) 方法。备注:BeanDefinitionRegistryPostProcessor 属于 BeanFactoryPostProcessor 子接口。先处理属于 BeanDefinitionRegistryPostProcessor 接口实例的 postProcessBeanFactory(beanFactory) 方法,然后获取容器注册的。对于这些 Bean 按照 PriorityOrdered 接口、Ordered、没有排序接口的实例分别进行处理。

3.4 Bean 级生命周期方法

可以理解为 Bean 类直接实现接口的方法,比如 BeanNameAware 、 BeanFactoryAware 、 ApplicationContextAware 、 InitializingBean 、 DisposableBean 等方法,这些方法只对当前 Bean 生效。

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

Aware 接口众多,这里同样通过分类的方式帮助大家记忆。Aware 接口具体可以分为两组,至于为什么这么分,详见下面的源码分析。如下排列顺序同样也是 Aware 接口的执行顺序,能够见名知意的接口不再解释。

Aware Group1
BeanNameAware
BeanClassLoaderAware
BeanFactoryAware
Aware Group2
EnvironmentAware
EmbeddedValueResolverAware
这个知道的人可能不多,实现该接口能够获取 Spring EL 解析器,用户的自定义注解需要支持 SPEL 表达式的时候可以使用,非常方便。
ApplicationContextAware(ResourceLoaderAware/ApplicationEventPublisherAware/MessageSourceAware)
这几个接口可能让人有点懵,实际上这几个接口可以一起记,其返回值实质上都是当前的 ApplicationContext 对象,因为 ApplicationContext 是一个复合接口,如下:

Aware 调用时机源码分析

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

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

关于 Aware 接口的执行顺序,其实只需要记住第一组在第二组执行之前就行了。

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

InitializingBean 对应生命周期的初始化阶段,在上面源码的 invokeInitMethods(beanName, wrappedBean, mbd); 方法中调用。
有一点需要注意,因为 Aware 方法都是执行在初始化方法之前,所以可以在初始化方法中放心大胆的使用 Aware 接口获取的资源,这也是我们自定义扩展 Spring 的常用方式。
除了实现 InitializingBean 接口之外还能通过注解或者 xml 配置的方式指定初始化方法,至于这几种定义方式的调用顺序其实没有必要记。因为这几个方法对应的都是同一个生命周期,只是实现方式不同,我们一般只采用其中一种方式。
DisposableBean 类似于 InitializingBean,对应生命周期的销毁阶段,以 ConfigurableApplicationContext#close() 方法作为入口,实现是通过循环取所有实现了 DisposableBean 接口的 Bean 然后调用其 destroy() 方法,感兴趣的可以自行跟一下源码。
3.5 Spring Bean 生命周期流程图

四、常用接口说明

4.1 BeanNameAware
该接口只有一个方法 setBeanName(String name) ,用来获取 bean 的 id 或者 name 。

4.2 BeanFactoryAware
该接口只有一个方法 setBeanFactory(BeanFactory beanFactory) ,用来获取 当前环境中的 BeanFactory 。

4.3 ApplicationContextAware
该接口只有一个方法 setApplicationContext(ApplicationContext applicationContext) ,用来获取 当前环境中的 ApplicationContext 。

4.4 InitializingBean
该接口只有一个方法 afterPropertiesSet() ,在 属性注入完成后调用 。

4.5 DisposableBean
该接口只有一个方法 destroy() ,在容器销毁的时候调用,在 用户指定的 destroy-method 之前调用 。

4.6 BeanPostProcessor
该接口有两个方法:

postProcessBeforeInitialization(Object bean, String beanName) :在 初始化之前 调用此方法
postProcessAfterInitialization(Object bean, String beanName) :在 初始化之后 调用此方法
通过方法签名我们可以知道,我们可以通过 beanName 来筛选出我们需要进行个性化定制的 bean。

4.7 InstantiationAwareBeanPostProcessor
该类是 BeanPostProcessor 的子接口,常用的有如下三个方法:

postProcessBeforeInstantiation(Class beanClass, String beanName) :在bean 实例化之前 调用
postProcessProperties(PropertyValues pvs, Object bean, String beanName) :在bean 实例化之后、设置属性前 调用
postProcessAfterInstantiation(Class beanClass, String beanName) :在bean 实例化之后 调用

五、代码演示

思路:创建一个类 UserBean ,让其实现几个特殊的接口,并分别在接口实现的构造器、接口方法中断点,观察线程调用栈,分析出 Bean 对象创建和管理关键点的触发时机。

5.1 UserBean 类

@Component
public class UserBean implements InitializingBean, BeanNameAware, DisposableBean, ApplicationContextAware {private int id;private String name;public UserBean(int id, String name) {this.id = id;this.name = name;System.out.println("2. 调用构造函数");}public int getId() {return id;}public void setId(int id) {this.id = id;System.out.println("5. 属性注入 id");}public String getName() {return name;}public void setName(String name) {this.name = name;System.out.println("5. 属性注入 name");}@Overridepublic void setBeanName(String name) {System.out.println("6. 调用 BeanNameAware.setBeanName() 方法");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {UserBean userBean = (UserBean) applicationContext.getBean("userBean");System.out.println(userBean);System.out.println("7. 调用 BeanNameAware.setBeanName() 方法");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("9. 调用 InitializingBean.afterPropertiesSet() 方法");}public void myInit() {System.out.println("10. 调用 init-method 方法");}@Overridepublic void destroy() throws Exception {System.out.println("12. 调用 DisposableBean.destroy() 方法");}public void myDestroy() {System.out.println("13. 调用 destroy-method 方法");}@Overridepublic String toString() {return "UserBean{" +"id=" + id +", name='" + name + '\'' +'}';}
}

5.2 InstantiationAwareBeanPostProcessor 接口实现类

@Component
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {if ("userBean".equals(beanName)) {System.out.println("1. 调用 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() 方法");}return null;}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if ("userBean".equals(beanName)) {UserBean userBean = (UserBean) bean;System.out.println("3. 调用 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation() 方法");System.out.println(userBean);}return true;}@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {if ("userBean".equals(beanName)) {System.out.println("4. 调用 InstantiationAwareBeanPostProcessor.postProcessProperties() 方法");}return null;}
}

5.3 BeanPostProcessor 接口实现类

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if ("userBean".equals(beanName)) {System.out.println("8. 调用 BeanPostProcessor.postProcessBeforeInitialization() 方法");}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if ("userBean".equals(beanName)) {System.out.println("11. 调用 BeanPostProcessor.postProcessAfterInitialization() 方法");}return bean;}
}

5.4 BeanFactoryPostProcessor 接口实现类

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {System.out.println("0. 调用 BeanFactoryPostProcessor.postProcessBeanFactory() 方法");}
}

5.5 applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd
"><bean class="com.riemann.test.MyInstantiationAwareBeanPostProcessor" /><bean id="userBean" class="com.riemann.test.UserBean" init-method="myInit" destroy-method="myDestroy"><!-- 构造函数注入 --><constructor-arg index="0" type="int"><value>1</value></constructor-arg><constructor-arg index="1" type="java.lang.String"><value>微信公众号【老周聊架构】</value></constructor-arg><!-- setter方法注入 --><property name="id" value="2"/><property name="name" value="riemann"/></bean><bean class="com.riemann.test.MyBeanPostProcessor" /><bean class="com.riemann.test.MyBeanFactoryPostProcessor" /></beans>

5.6 测试类

public class BeanLifeCycleTest {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");UserBean user = (UserBean) applicationContext.getBean("userBean");((AbstractApplicationContext) applicationContext).close();}
}

5.7 控制台结果打印

谢谢~see you ~

文章来源:https://www.tuicool.com/articles/3myyeaj

带你读懂Spring Bean 的生命周期,嘿,就是玩儿~相关推荐

  1. 带你读懂Spring 事务——事务的隔离级别(超详细,快藏)

    不了解事务的铁汁可以先看前两篇,讲的超详细,有问题还请您指点一二 带你读懂Spring 事务--认识事务 带你读懂Spring 事务--事务的传播机制(藏) 特别提示:本文所进行的实验都是在MySQL ...

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

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

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

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

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

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

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

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

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

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

  7. 再聊Spring Bean的生命周期

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

  8. 【Spring Bean的生命周期】

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

  9. 简述 Spring Bean的生命周期

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

最新文章

  1. Gradle之FTP文件下载
  2. nginx php post限制,叫你如何修改Nginx与PHP的文件上传大小限制
  3. python logging 毫秒级别的时间打印
  4. 关于Eclipse创建Android项目时,会多出一个appcompat_v7的问题
  5. 20、java中的类加载机制
  6. 机器学习从理论到工程的第二步-开发环境与工具篇
  7. 或许是单选的飞鸽传书
  8. 奇偶数判断(信息学奥赛一本通-T1041)
  9. c语言程序40例,C语言程序讲解40例.pdf
  10. mysql rr gap nextkey_mysql中的各种锁把我搞糊涂啦~
  11. Oracle SQL的优化 【转】
  12. [TJOI2017]DNA
  13. deebot扫地机器人响四声_中国智能扫地机器人重点制造基地企业名录(2020年版)...
  14. python高阶函数心得体会_Python高阶函数总结
  15. 照片审核处理工具_不需要Photoshop,这5款在线处理图片工具能帮你好好处理照片!...
  16. rust服务器显示长度,rust服务器设置倍率
  17. 【word毕业论文排版(1)】尾注的删除
  18. <一>Android Audio音频框架
  19. ddos攻击怎么防御,一文了解如何防御DDoS攻击
  20. AutoCAD输入netload命令后不弹出菜单而是一直显示程序集文件名解决方案

热门文章

  1. 利润中心, 成本中心, 工作中心
  2. 新宝来引擎盖怎么打开
  3. CLASS ALV Event
  4. 食品新消费的2021:站在逻辑跑通与成为品牌的隧道期
  5. 从双十一强化体验认知,看苏宁的“自增强回路”增长飞轮
  6. php函数计算加法,JavaScript_javascript实现一个数值加法函数,废话不多说,直接奉上代码 JS - phpStudy...
  7. linux中sh+$0,浅谈linux中shell变量$#,$@,$0,$1,$2的含义解释
  8. java graphics透明_如何使用Graphics对象g绘制透明形状?
  9. 怎么看承重_怎么选购到一个好的工具柜,这些方面要考虑
  10. c语言第七周答案,2017-mooc-C语言-第七周-答案