SpringBean的生命周期详解
在Spring生态环境中,SpringBean是除了IoC、AOP两个核心概念之外另一个重要概念,同时SpringBean的生命周期也是面试现场中的常客,而这一概念在Spring中又是零星分散在容器的各个阶段(源码的各个类中),查询起来比较复杂。因此,这里对SpringBean的生命周期进行一系列的总结,以供参考。
文章目录
Spring Bean的生命周期概述
Spring Bean的生命周期包含一个Bean对象从无到有再到消失的全过程,概括的将包括配置、创建、使用、销毁4个阶段。各个阶段又包含多个步骤,配置阶段包括定义、解析、注册、合并4步;创建阶段包括类加载、实例化、属性赋值、初始化4步;销毁阶段包括准备、销毁2步。注意,SpringBean的垃圾回收(GC)严格来说也属于Bean的销毁,但该步骤属于JVM处理过程,不在Spring容器的控制范围内,本文不考虑在内。
下图是对Spring Bean的生命周期各阶段的简要说明(包括GC)
注意:上图中所进行的只是宏观上的步骤划分,具体代码中根据配置的不同,各个阶段、各个步骤还存在数量不等的回调位置,本文将针对一些典型的回调进行说明。
配置阶段
定义
配置阶段的主要工作就是完成Bean的配置工作,我们在开发过程中进行的配置(配置文件、配置类)既是这一阶段的第一步,定义。常见的定义方式有xml文件、properties文件、Java类3种,以下一一说明。
创建一个简单类,以下所有配置都是基于这个类
public class User {
private Long id;
private String name;
//省略构造方法
//省略getter、setter方法
}
基于XML的Bean配置方式
<!-- 完整的xml配置文件 -->
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="xml-user" class="top.sunyog.beans.pojo.User">
<property name="id" value="100"/>
<property name="name" value="xml配置名称"/>
</bean>
</beans>
基于properties的配置方式
#properties配置文件内容
user.id=100
user.(class)=top.sunyog.beans.pojo.User
user.name=properties配置名称
基于Java配置类
@Configuration
public class UserConfig{
@Bean(name="config-class-user")
public User createUser(){
return new User(100L,"java配置类");
}
}
以上三种方式任选一种即可完成对User
这个Bean的定义。这里只介绍最简单的Bean定义,实际操作过程中根据需求不同还会用到如:作用域、autowire
模式、延迟加载、primary
、工厂方法、FactoryBean
、初始化方法、销毁方法等配置项。
定义完成后待容器启动时就会读取这些Bean的定义进行解析。
解析
解析阶段的工作是将上一步定义的Bean解析为BeanDefinition
对象,以供后续步骤使用。BeanDefinition
是对SpringBean的详细描述,Spring容器根据这个详细描述即可完成对这个Bean对象的管理(BeanDefinition
是Spring管理Bean的依据)
根据Spring Bean的定义方式不同,解析的方式也不同,最终产生的BeanDefinition
的具体类也是不同的。
- 基于xml配置的Bean在
XmlBeanDefinitionReader#loadBeanDefinitions
方法中被转化为GenericBeanDefintion
对象; - 基于properties文件配置的Bean在
PropertiesBeanDefinitionReader#loadBeanDefinitions
方法中被转化为GenericBeanDefintion
对象; - 基于Java注解配置的bean在
AnnotatedBeanDefinitionReader#doRegisterBean
方法中被解析为AnnotatedGenericBeanDefinition
。具体解析过程如下图所示:
注册
注册的意思是将上一步解析出的BeanDefinition
对象注册到Spring容器中,通常都是调用BeanDefinitionRegistry#doRegisterBeanDefinition
方法完成注册工作,这里与Bean的定义方式无关,BeandefinitionRegistry
是一个接口,最常用的实现类为DefaultListableBeanFactory
注册工作的主要步骤为:
合并
合并是针对设置了parent
参数的Bean进行合并,将parent中的配置和bean本身的配置合并到一起,组合为RootBeanDefinition
。parent
参数的设置方式如下:
<bean id="bean-1" .../>
<bean id="bean-2" parent="bean-1" .../>
BeanDefinition
合并的工作主要在AbstractBeanFactory#getMergedBeanDefinition
方法中进行,该方法的主要工作流程如下
普通Bean定义的解析、注册、合并工作在refresh()
中的AbstractApplicationContext#invokeBeanFactoryPostProcessors
步骤中进行,具体的代码可以在PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
中查看
创建阶段
Bean的创建阶段的大部分工作(这里是大部分而不是全部,因为有一个特殊的步骤,在特殊的初始化后回调一节中说明)都在AbstractAutowireCapableBeanFactory#createBean
和AbstractAutowireCapableBeanFactory#doCreateBean
方法中进行,这里贴出了方法的部分源码,并对其做了说明
//AbstractAutowireCapableBeanFactory#createBean
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
//类加载
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
...
//实例化(前)
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
...
//实例化 - 属性赋值 - 初始化
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
...
return beanInstance;
}
//AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
//实例化
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
...
//属性赋值(包括:实例化后回调、属性赋值、属性回调)
populateBean(beanName, mbd, instanceWrapper);
//初始化(包括:Aware回调、初始化前回调、初始化方法、初始化后回调)
exposedObject = initializeBean(beanName, exposedObject, mbd);
...
//销毁阶段注册
registerDisposableBeanIfNecessary(beanName, bean, mbd);
...
return exposedObject;
}
从以上代码可以看出,本文对bean创建阶段所做的步骤划分就是根据方法的调用顺序进行划分的,即:
类加载 − − > 实例化 − − > 属性赋值 − − > 初始化 类加载-->实例化-->属性赋值-->初始化 类加载−−>实例化−−>属性赋值−−>初始化
类加载
类加载的工作主要是调用AbstractBeanFactory#resolveBeanClass
方法获取到bean的类对象。
实例化
实例化阶段主要是通过AbstractAutowireCapableBeanFactory#createBeanInstance
执行bean的实例化方法(构造方法),涉及到的实例化方式有以下四种:
- 通过
Supplier#get
创建,典型的方式是设置了AbstractBeanDefinition#setInstanceSupplier
- 通过
ConstructorResolver#instantiateUsingFactoryMethod
创建,典型的方式是通过设置factory-method=...
、或通过FactoryBean
接口创建bean - 通过
ConstructorResolver#autowireConstructor
创建,典型的方式是构造器注入 - 通过
InstantiationStrategy
接口创建,如SimpleInstantiationstrategy#instantiate
。典型的方式是Bean工厂的实例化、set注入
通过以下表格可以直观的看到不同配置的bean和实例化方法的对应关系
序号 | 配置方式 | 实力化方法 |
---|---|---|
1️⃣ | 构造器注入 | ConstructorResolver#autowireConstructor |
2️⃣ | set注入 | SimpleInstantiationstrategy#instantiate |
3️⃣ | 自定义工厂方法 | ConstructorResolver#instantiateUsingFactoryMethod |
4️⃣ | 自定义工厂类的工厂方法 | ConstructorResolver#instantiateUsingFactoryMethod |
5️⃣ | 实现FactoryBean | ConstructorResolver#instantiateUsingFactoryMethod |
6️⃣ | 设置Supplier | Supplier#get |
在实例化方法执行前后,各存在一个典型的扩展点(方法回调),实例化前的回调在AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
方法中可以看到,调用了InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
方法,实例化后的回调在属性赋值阶段(populateBean
方法开头)调用。因此实现InstantiationAwareBeanPostProcessor
接口的两个对应方法,并注册进Spring容器中即可实现对Bean初始化前、后的控制,通过以下代码即可实现
@Bean
public InstantiationAwareBeanPostProcessor myInstantiation(){
return new InstantiationAwareBeanPostProcessor(){
//注意,此方法的返回值如果非空,则会跳过实例化、属性赋值、初始化阶段,直接进入实例化后的回调,然后返回bean
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("bean对象实例化前的操作...");
return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);
}
//注意,此方法返回值如果是false,则会跳过属性赋值阶段,进入初始化阶段
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println("bean对象实例化后的操作...");
return true;
}
};
}
属性赋值
bean的实例化完成后会得到一个BeanWrapper
类型的对象,其中的wrappedObject
属性就是所需的bean对象,但是这时的bean对象只执行了构造函数,其内部的属性还都是空值,(构造函数注入的除外)这就需要对bean对象进行属性赋值
属性赋值的工作在AbstractAutowireCapableBeanFactory#populateBean
方法中进行。populateBean
方法的工作流程如下
从上图中可以看出,在bean的autowire之后和setter方法赋值之前,还存在一个扩展点(回调),在这个回调中可以对pvs进行扩展,这里需要注意需要将pvs参数返回,pvs方法将被用于setter方法赋值。引入此回调的方法如下
@Bean
public InstantiationAwareBeanPostProcessor myInstantiation(){
return new InstantiationAwareBeanPostProcessor(){
//此方法将为SuperUser类型的bean添加addr属性的值为beijing
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
final MutablePropertyValues mpvs;
if (pvs instanceof MutablePropertyValues){
mpvs= ((MutablePropertyValues) pvs);
}else{
mpvs=new MutablePropertyValues();
}
if (bean instanceof SuperUser){
mpvs.add("addr","beijing");
}
return mpvs;
}
};
}
初始化
初始化方法
初始化的工作主要是执行配置的初始化方法,初始化方法有三种主要的配置方式,分别是
- 使用
@PostConstruct
注解 - 实现
InitializingBean
接口 - 配置自定义初始化方法,如
@Bean(initMethod="")
、<bean ... initMethod=""/>
以上三种方式可以同时配置在同一个bean对象中,如果同时出现会按照以上1~3的顺序依次执行。初始化工作在AbstractAutowireCapableBeanFactory#initializeBean
方法中进行,这里贴出这个方法的部分代码
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
//beanfactory中的aware回调
invokeAwareMethods(beanName, bean);
//初始化前回调
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
//初始化方法
invokeInitMethods(beanName, wrappedBean, mbd);
//初始化后回调
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}
一个典型问题:
如果进入
invokeInitMethods
这个方法可能会发现一个问题,该方法只调用了自定义初始化方法和InitializingBean#afterPropertiesSet
两个方法,而没有@PostConstruct
标注的方法。而且,如果是通过BeanFactory
启动的容器(如DefaultListableBeanFactory
),这时@PostConstruct
注解标注还会失效。
这是因为@PostConstruct
标注的方法是在CommonAnnotationBeanPostProcessor
中注册进容器的,而CommonAnnotationBeanPostProcessor
又是在AnnotationConfigUtils
工具类中被注册进容器,以上这些步骤都是ApplicationContext
中进行的。所以@PostConstruct
注解的方法在初始化前回调中被调用,这也是为什么它在三种初始化方法中第一个被调用的原因,而BeanFactory
启动的容器中没有注册过CommonAnnotationBeanPostProcessor
这个后置处理器,因此也就无法执行@PostConstruct
初始化方法。
BeanPostProcessor回调
初始化前和初始化后的回调可以通过实现BeanPostProcessor
接口的方式控制,在实例化步骤中提到的InstantiationAwareBeanPostProcessor
回调是BeanPostProcessor
的子接口,因此也可以实现对初始化的控制,不过不建议使用这种方式,因为这容易造成对实例化和初始化两个步骤在理解上的混乱。具体的代码示例如下:
@Bean
public BeanPostProcessor myBeanPostProcessor(){
return new BeanPostProcessor(){
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化前回调");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化后回调");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
};
}
内置的9种Aware接口
在Spring中内置了9种通知接口,它们都是org.springframework.context.Aware
的子接口,具体功能如下:
序号 | 接口名 | 功能 |
---|---|---|
1️⃣ | BeanNameAware | 使bean对象可以访问到它的beanName |
2️⃣ | BeanClassLoaderAware | 使bean对象可以访问到bean的类加载器 |
3️⃣ | BeanFactoryAware | 使bean对象可以访问到beanFactory |
4️⃣ | EnvironmentAware | 使bean对象可以访问到环境信息 |
5️⃣ | EmbeddedValueResolverAware | 使bean对象访问到StringValueResolver |
6️⃣ | ResourceLoaderAware | 使bean对象访问到资源加载器 |
7️⃣ | ApplicationEventPublisherAware | 使bean对象访问到事件发布通知 |
8️⃣ | MessageSourceAware | 使bean对象访问到消息 |
9️⃣ | ApplicationContextAware | 使bean对象访问到容器上下文 |
其中前三种是BeanFactory
控制的通知接口,在invokeAwareMethods
方法中调用,后6种属于ApplicationContext
控制的通知,在ApplicationContextAwareProcessor
中的初始化前置方法调用,这也就控制了这9种通知接口按照表格中1~9的顺序调用。由此也可以看出,如果在ApplicationContextAwareProcessor
之前注册了其他的后置处理器,以上9种通知中的3️⃣和4️⃣不一定是连续的,中间可能穿插着其他的后置处理器方法。和@PostConstruct
初始化方法一样,如果通过BeanFactory
启动容器,后6种通知回调也会失效。
特殊的初始化后回调
有一个特殊的初始化后置回调方法,SmartInitializingSingleton#afterSingletonsInstantiated
从名称可以看出,它在单例对象创建完成后执行。在Spring源码的DefaultListableBeanFactory#preInstantiateSingletons
方法的最后一步中完成,此方法在AbstractApplicationContext#finishBeanFactoryInitialization
中调用(应用上下文启动时)。因此它在BeanFactory
容器中的创建阶段之后执行。这也是它只能控制lazy=false
且作用域为singleton
的bean的原因。简单的代码实现如下:
public class BeanObjClass implements SmartInitializingSingleton{
private Long id;
//省略getter、setter方法
@Override
public void afterSingletonsInstantiated() {
System.out.println("方法调用: SmartInitializingSingleton#afterSingletonsInstantiated");
}
}
//省略此bean对象的相关配置
使用阶段
使用阶段主要是对bean对象的依赖查找和依赖注入工作,这里不做具体说明。
销毁阶段
常见的对SpringBean对象进行销毁的操作有
- 执行
ConfigurableBeanFactory#destroyBean
方法 - 容器上下文关闭,即调用
AbstractApplicationContext#close
方法
注意:以上这两种方法只能销毁作用域为singleton
的bean对象。作用域为prototype
的bean没有销毁生命周期,即便设置了销毁方法也无法被调用到,prototype
作用域的Bean的销毁都是GC控制。
相较于配置阶段和创建阶段,销毁阶段的处理逻辑比较简单,全部集中在DisposableBeanAdapter#destroy
方法中,这里贴出部分源码
public void destroy() {
//循环调用销毁前置处理方法
if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
processor.postProcessBeforeDestruction(this.bean, this.beanName);
}
}
//DisposableBean#destroy
((DisposableBean) this.bean).destroy();
//自定义销毁方法
invokeCustomDestroyMethod(this.destroyMethod);
}
准备
这一步的主要工作是调用一系列DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
方法,可以通过实现这一接口方法实现对销毁前工作的扩展,主要方式和BeanPostProcessor
接口相似,代码如下
@Bean
public DestructionAwareBeanPostProcessor myDestructionProcessor(){
return new DestructionAwareBeanPostProcessor(){
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
System.out.println("销毁前回调..."):
}
};
}
这里有一个特殊的DestructionAwareBeaenPostProcessor
实现类,CommonAnnotationBeanPostProcessor
这个类除了在创建阶段发挥作用,在bean的销毁阶段也有它的一席之地,它的工作就是将@PreDestroy
注解注册为bean的销毁注解,因此@PreDestroy
能够控制bean的销毁。
这里就产生了一个新问题
自定义的
DestructionAwareBeanPostProcessor
回调在语义上是销毁前回调,而@PreDestroy
也是bean的销毁前置方法,有时候自定义的销毁前回调在@PreDestroy
注解的方法之后才会被调用,有时候却是在之后
其实两者没有本质的区别,实际上都是通过DestructionAwareBeanPostProcessor
接口实现的,之所以会产生顺序问题主要是因为注册的顺序不同导致的,在实际操作中可以通过获取CommonAnnotationBeanPostProcessor
的注册列表下标,根据这个下标控制自定义处理器的插入位置即可控制两者的执行顺序。代码示例如下
public static void main(String[] args) {
...
DefaultListableBeanFactory beanFactory=(DefaultListableBeanFactory)context.getBeanFactory();
List<BeanPostProcessor> processors = beanFactory.getBeanPostProcessors();
//获取CommonAnnotationBeanPostProcessor下标
int index = processors.indexOf(beanFactory.getBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
//将DestructionAwareBeanPostProcessor添加到CommonAnnotationBeanPostProcessor之前
processors.add(index,new DestructionAwareBeanPostProcessor(){
//自定义销毁前回调
@Override
...
});
}
销毁
Spring Bean的销毁方法有3种常用的定义方式
- 使用
@PreDestroy
注解标注方法 - Bean对象类实现
DisposableBean
接口 - 自定义销毁方法,如
@Bean(destroyMethod="")
或<bean destroyMethod=""/>
根据DisposableBeanAdapter#destroy
方法的执行逻辑,以上三种定义方式可以共存,而且按照1~3的顺序执行。和初始化方法类似,如果是通过BeanFactory
启动的容器,@PreDestroy
注解的方法会失效,原因见初始化方法一章
GC阶段
GC阶段主要是JVM的垃圾收集工作,这里不做说明。
具体的垃圾收集策略及JVM工作原理可查看以下内容了解:
【注】原创文章,欢迎点赞、转发、评论。转载请先与作者联系