likes
comments
collection
share

浅谈 Spring Bean 的生命周期

作者站长头像
站长
· 阅读数 18

1. Bean 的生命周期概述

区别于普通的 Java 对象需要通过 new 创建对象,Spring 的 Bean 由 IoC 容器进行实例化、组装以及管理的。也就是说 Bean 的生命周期完全由 IoC 容器控制

Spring 容器只能管理 单例(singleton) 作用域的 Bean 的完整生命周期,对于 原型(prototype) 作用域的 Bean,Spring 容器只创建 bean 的实例后便会返回给用户,剩余的生命周期由用户控制。所以 Bean 的生命周期主要指的是 singleton 作用域 的 Bean。

为什么要了解 Bean 的生命周期?

通过了解 Bean 的生命周期可以了解 Bean 是什么时候被创建的,什么时候初始化,什么时候被销毁以及整个 Bean 的生命周期中哪些阶段可以使用哪些增加方法。

这样在实际开发中,可以利用 Bean 的生命周期的指定时刻完成一些自定义的操作。同时面试中 Spring Bean 的生命周期也是常问的一个内容,所以很有必要了解一下。

Bean 的生命周期主要包括四个部分

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

AbstractApplicationContextrefresh() 方法为入口,AbstractAutowireCapableBeanFactorydoCreateBean 方法完成对 Bean 的实例化、属性注入以及初始化。(具体流程可以看 3.2 的具体流程梳理)

浅谈 Spring Bean 的生命周期

AbstractApplicationContextclose() 方法为入口,在 DisposableBeanAdapterdestory() 方法完成对 Bean 的销毁。(具体流程可以看 3.2 的具体流程梳理)

浅谈 Spring Bean 的生命周期


2. 相关接口及方法

Bean 的生命周期中使用到了 Aware 类型的相关接口、InitializingBean/DisposableBean 接口以及一些起到增强作用的接口。了解这些接口可以帮助大家更好地了解 Bean 的生命周期以及更好地拓展定制自己的 Bean 对象。

2.1 各种 Aware 类型的接口

实现 Aware 接口的目的是让程序可以拿到 Spring 容器的当前的运行环境(如当前 Bean 的名称、当前的 BeanFactory、当前的 ApplicationContext 等等资源)。这类接口的调用时机是在 Bean 实例化、属性注入之后,初始化之前

接下来列出一些常用的子接口,有兴趣的可以从源码中查看还有哪些接口(基本都是见名知意):

BeanNameAware 接口

接口中只有一个方法 void setBeanName(String name) 用于获取当前 Bean 的名称。

浅谈 Spring Bean 的生命周期

BeanFactoryAware 接口

接口中只有一个方法 void setBeanFactory(BeanFactory beanFactory) 用于获取当前的 BeanFactory。

浅谈 Spring Bean 的生命周期

ApplicationContextAware 接口

接口中只有一个方法 void setApplicationContext(ApplicationContext applicationContext) 用于获取当前的 ApplicationContext。

浅谈 Spring Bean 的生命周期


2.2 InitializingBean/DisposableBean 接口

实现InitializingBean/DisposableBean 接口能够实现自定义初始化方法以及销毁方法。这两个接口的调用时机分别在初始化阶段与销毁阶段

InitializingBean 接口

接口中只有一个方法 void afterPropertiesSet() 用于自定义初始化行为,在属性注入阶段后执行。

浅谈 Spring Bean 的生命周期

DisposableBean 接口

接口中只有一个方法 void destroy() 用于自定义销毁行为,在销毁阶段执行。

浅谈 Spring Bean 的生命周期

除了接口实现方式外,还有两种可以实现自定义初始化以及销毁的方式:

  • 使用 XML 配置文件 或者 Java 配置类 对 Bean 中的 init-methoddestroy-method 属性进行配置(基于 Bean 自身的初始化和销毁)
  • 使用 注解 @PostConstruct@PreDestroy 修饰方法(作用于servlet生命周期的注解)

2.3 增强方法

BeanFactoryPostProcessor 接口

实现该接口可以增强的方面是:在 BeanFactory 已经初始化而 Bean 实例化之前调用该接口的方法可以修改或添加 Bean 的定义。所以该接口的调用时机是在 Bean 实例化之前

接口中只有一个方法 void postProcessBeanFactory(ConfigurableListableBeanFactory var1) 用于在 Bean 实例化之前修改 Bean 的定义。

浅谈 Spring Bean 的生命周期

BeanPostProcessor 接口

实现该接口能增强的方面是:在 Bean 实例化后,在初始化阶段的前后自定义行为。可以根据接口方法中的参数来筛选需要自定义行为的 Bean。该接口的调用时机是在 初始化阶段的前后

该接口有两个方法分别在初始化前和初始化后执行:

  • postProcessBeforeInitialization(Object bean, String beanName):初始化前调用
  • postProcessAfterInitialization(Object bean, String beanName):初始化后调用

浅谈 Spring Bean 的生命周期

InstantiationAwareBeanPostProcessor 接口

实现该接口能增强的方面是:在目标 Bean 实例化的前后可以自定义行为以及在属性注入前可以修改 Bean 的属性设置。

该接口有三个方法(其实还有两个来自于继承 BeanPostProcessor 接口的方法):

  • postProcessBeforeInstantiation(Class<?> beanClass, String beanName):在 Bean 实例化之前调用
  • postProcessAfterInstantiation(Object bean, String beanName):在 Bean 实例化之后调用
  • postProcessProperties(PropertyValues pvs, Object bean, String beanName):在 Bean 实例化之后,属性注入之前调用

浅谈 Spring Bean 的生命周期


3. 浅谈生命周期的具体流程

3.1 通过例子展示具体流程

通过一个例子来体现 Bean 的生命周期 的具体流程以及相关接口对应的切入时机

MyBean 类

/**
 * Bean对象
 * @author 单程车票
 */
public class MyBean implements InitializingBean, DisposableBean, BeanNameAware {

    private Integer id;

    public MyBean(Integer id) {
        this.id = id;
        System.out.println("过程3(实例化阶段中):调用构造方法,当前id为" + this.id + "(可以发现id此时并不是配置文件的1001,而是增强方法修改后的1002)");
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
        System.out.println("过程6(属性注入阶段中):属性注入阶段,注入后id为" + this.id + "(可以发现id此时并不是配置文件的1003,而是增强方法修改后的1004)");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("过程7(属性注入阶段后):获取当前Bean的名称:" + name);
    }

    public void myInit() {
        System.out.println("过程10(初始化阶段中):调用Bean自身属性 init-method 的初始化方法");
    }

    public void myDestroy() {
        System.out.println("过程13(销毁阶段中):调用Bean自身属性 destroy-method 的销毁方法");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("过程9(初始化阶段中):调用 InitializingBean接口 的初始化方法");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("过程12(销毁阶段中):调用 DisposableBean接口 的销毁方法");
    }
}

实现 BeanFactoryPostProcessor 接口

/**
 * 对实例化前进行增强
 * @author 单程车票
 */
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanFactory.getBeanDefinition("myBean");
        ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
        constructorArgumentValues.addIndexedArgumentValue(0, 1002);
        // 将原本配置文件配置的值1001改为1002(这里使用了ConstructorArgumentValues有兴趣的可以自行了解一下)
        beanDefinition.setConstructorArgumentValues(constructorArgumentValues);
        System.out.println("过程1(实例化阶段前): 调用 BeanFactoryPostProcessor.postProcessBeanFactory 方法进行增强,将id值改为1002");
    }
}

实现 BeanPostProcessor 接口

/**
 * 对初始化阶段前后进行增强
 * @author 单程车票
 */
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("myBean".equals(beanName)) {
            System.out.println("过程8(属性注入阶段后,初始化阶段前):调用 BeanPostProcessor.postProcessBeforeInitialization 方法进行增强");
        }
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("myBean".equals(beanName)) {
            System.out.println("过程11(初始化阶段后):调用 BeanPostProcessor.postProcessAfterInitialization 方法进行增强");
        }
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

实现 InstantiationAwareBeanPostProcessor 接口

/**
 * 实现InstantiationAwareBeanPostProcessor接口
 * @author 单程车票
 */
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if ("myBean".equals(beanName)) {
            System.out.println("过程2(实例化阶段前):调用 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation 方法进行增强");
        }
        return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if ("myBean".equals(beanName)) {
            System.out.println("过程4(实例化阶段后):调用 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation 方法进行增强");
        }
        return InstantiationAwareBeanPostProcessor.super.postProcessAfterInstantiation(bean, beanName);
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if ("myBean".equals(beanName)) {  // 对 Bean 再次修改 id 属性
            PropertyValue propertyValue = pvs.getPropertyValue("id");
            assert propertyValue != null;
            propertyValue.setConvertedValue(1004);
            System.out.println("过程5(实例化阶段后,属性注入阶段前):调用 InstantiationAwareBeanPostProcessor.postProcessProperties 方法进行增强,将id属性改为1004");
        }
        return InstantiationAwareBeanPostProcessor.super.postProcessProperties(pvs, bean, beanName);
    }
}

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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 这里需要注意加上 Bean自身的初始化方法和销毁方法 -->
    <bean id="myBean" class="com.xqsr.springtest.bean.MyBean" init-method="myInit" destroy-method="myDestroy">
        <!-- 这里需要注意构造器(实例化)的id值为1001 -->
        <constructor-arg name="id" value="1001"/>
        <!-- 这里需要注意setter(属性注入)的id值为1001 -->
        <property name="id" value="1003"/>
    </bean>

    <bean class="com.xqsr.springtest.bean.MyBeanFactoryPostProcessor"/>
    <bean class="com.xqsr.springtest.bean.MyBeanPostProcessor"/>
    <bean class="com.xqsr.springtest.bean.MyInstantiationAwareBeanPostProcessor"/>

</beans>

测试类

/**
 * 测试类
 * @author 单程车票
 */
public class Application {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        MyBean bean = (MyBean) applicationContext.getBean("myBean");
        ((AbstractApplicationContext) applicationContext).close();
    }
}

执行结果

浅谈 Spring Bean 的生命周期


3.2 具体流程梳理

通过上面的例子可以梳理出下面这张 Bean 的生命周期 具体流程图

浅谈 Spring Bean 的生命周期

接下来从源码的角度对流程图的每一个部分进行剖析

从源码看 BeanFactoryPostProcessor # postProcessBeanFactory() 在实例化前执行

浅谈 Spring Bean 的生命周期

浅谈 Spring Bean 的生命周期

通过从 refresh() 入口进入按照上面图中的流程可以找到调用 postProcessBeanFactory() 的方法,该方法是在 invokeBeanFactoryPostProcessors(beanFactory) 进入的,而实例化阶段是从 finishBeanFactoryInitialization(beanFactory) 进入的,所以可以说明 BeanFactoryPostProcessor # postProcessBeanFactory() 要先于实例化执行。

浅谈 Spring Bean 的生命周期


从源码看 InstantiationAwareBeanPostProcessor # postProcessBeforeInstantiation() 在实例化前执行

浅谈 Spring Bean 的生命周期

浅谈 Spring Bean 的生命周期

通过上面的流程图可以找到调用 postProcessBeforeInstantiation() 的方法是在 resolveBeforeInstantiation(beanName, mbdToUse) 进入的,而实例化阶段是在 doCreateBean(beanName, mbdToUse, args) 进入的,所以可以说明 postProcessBeforeInstantiation() 要先于实例化执行。

浅谈 Spring Bean 的生命周期


从源码看 实例化执行位置 与 属性注入执行位置

浅谈 Spring Bean 的生命周期

实例化阶段发生在 AbstractAutowireCapableBeanFactorycreateBeanInstance() 方法中。

属性注入阶段发生在 AbstractAutowireCapableBeanFactorypopulateBean() 方法中。

浅谈 Spring Bean 的生命周期


从源码看 InstantiationAwareBeanPostProcessor # postProcessAfterInstantiation()InstantiationAwareBeanPostProcessor # postProcessPropertyValues() 属性注入阶段前执行

浅谈 Spring Bean 的生命周期

通过上面的流程图可以找到 postProcessAfterInstantiation()postProcessPropertyValues()populateBean() 属性注入方法内被调用。

浅谈 Spring Bean 的生命周期

  • postProcessAfterInstantiation() 方法如果找到指定的 bean 就会返回 false,会直接返回不再执行后续方法内容,也就是说后续的属性填充和依赖注入就不会被执行,所以可以看出 postProcessAfterInstantiation() 执行在属性注入阶段前。
  • postProcessPropertyValues() 方法同样发生在 属性填充 之前,如果返回null则不会执行后续的属性填充,如果不会为null,说明有额外添加的属性需要填充,后续方法会执行属性填充。

从源码看 初始化阶段及其前后发生的调用

浅谈 Spring Bean 的生命周期

根据上面的流程图可以找到初始化阶段进入的入口 initializeBean() 方法,具体代码如下图:

浅谈 Spring Bean 的生命周期

  1. 先看 Aware 接口的调用

    浅谈 Spring Bean 的生命周期 除了上面 BeanXXXAware 接口的调用,如 ApplicationContextAware 接口的调用是发生在 applyBeanPostProcessorsBeforeInitialization 方法中的,也就是是通过 BeanPostProcessor的postProcessBeforeInitialization() 实现调用的,具体的可以自己跟一下源码。 浅谈 Spring Bean 的生命周期

  2. BeanPostProcessor 的两个实现方法

    这两个实现方法分别通过 applyBeanPostProcessorsBeforeInitializationapplyBeanPostProcessorsAfterInitialization 方法调用,也可以看出一个在初始化前一个在初始化后执行。 浅谈 Spring Bean 的生命周期 浅谈 Spring Bean 的生命周期

  3. invokeInitMethods 初始化方法

    invokeInitMethods 初始化方法中调用了 InitializingBean 的自定义初始化方法 与 Bean自身属性中的 init-method 指定的方法。 浅谈 Spring Bean 的生命周期


从源码看 销毁阶段

浅谈 Spring Bean 的生命周期

根据上面的流程图最终会找到 DisposableBeanAdapterdestory() 方法,具体代码如下:

浅谈 Spring Bean 的生命周期

通过 destory() 方法也可以看到 销毁阶段 先执行 DisposableBean 的销毁方法,再执行 Bean 自身属性 destory-method 指定的方法。