SpringBean的生命周期详解

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)

SpringBean的生命周期

图一:Spring Bean生命周期各阶段说明

注意:上图中所进行的只是宏观上的步骤划分,具体代码中根据配置的不同,各个阶段、各个步骤还存在数量不等的回调位置,本文将针对一些典型的回调进行说明。

配置阶段

定义

配置阶段的主要工作就是完成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。具体解析过程如下图所示:

Bean的解析

图二:Spring BeanDefinition的解析流程图

注册

注册的意思是将上一步解析出的BeanDefinition对象注册到Spring容器中,通常都是调用BeanDefinitionRegistry#doRegisterBeanDefinition方法完成注册工作,这里与Bean的定义方式无关,BeandefinitionRegistry是一个接口,最常用的实现类为DefaultListableBeanFactory

注册工作的主要步骤为:

Bean的注册

图三:Spring BeanDefinition注册流程图

合并

合并是针对设置了parent参数的Bean进行合并,将parent中的配置和bean本身的配置合并到一起,组合为RootBeanDefinitionparent参数的设置方式如下:

<bean id="bean-1" .../>
<bean id="bean-2" parent="bean-1" .../>

BeanDefinition合并的工作主要在AbstractBeanFactory#getMergedBeanDefinition方法中进行,该方法的主要工作流程如下

BeanDefinition的合并

图四:Spring BeanDefinition合并流程图

普通Bean定义的解析、注册、合并工作在refresh()中的AbstractApplicationContext#invokeBeanFactoryPostProcessors步骤中进行,具体的代码可以在PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors中查看

创建阶段

Bean的创建阶段的大部分工作(这里是大部分而不是全部,因为有一个特殊的步骤,在特殊的初始化后回调一节中说明)都在AbstractAutowireCapableBeanFactory#createBeanAbstractAutowireCapableBeanFactory#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和实例化方法的对应关系

表一:Spring Bean的实例化方法分类表
序号配置方式实力化方法
1️⃣构造器注入ConstructorResolver#autowireConstructor
2️⃣set注入SimpleInstantiationstrategy#instantiate
3️⃣自定义工厂方法ConstructorResolver#instantiateUsingFactoryMethod
4️⃣自定义工厂类的工厂方法ConstructorResolver#instantiateUsingFactoryMethod
5️⃣实现FactoryBeanConstructorResolver#instantiateUsingFactoryMethod
6️⃣设置SupplierSupplier#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的属性赋值

图五: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;
        }
    };
}

初始化

初始化方法

初始化的工作主要是执行配置的初始化方法,初始化方法有三种主要的配置方式,分别是

  1. 使用@PostConstruct注解
  2. 实现InitializingBean接口
  3. 配置自定义初始化方法,如@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种常用的定义方式

  1. 使用@PreDestroy注解标注方法
  2. Bean对象类实现DisposableBean接口
  3. 自定义销毁方法,如@Bean(destroyMethod="")<bean destroyMethod=""/>

根据DisposableBeanAdapter#destroy方法的执行逻辑,以上三种定义方式可以共存,而且按照1~3的顺序执行。和初始化方法类似,如果是通过BeanFactory启动的容器,@PreDestroy注解的方法会失效,原因见初始化方法一章

GC阶段

GC阶段主要是JVM的垃圾收集工作,这里不做说明。

具体的垃圾收集策略及JVM工作原理可查看以下内容了解:

JVM内存模型

垃圾回收算法

垃圾收集器


【注】原创文章,欢迎点赞、转发、评论。转载请先与作者联系

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李奇技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值