Java 面试必备的 Spring Bean 生命周期总结

前言

Spring 作为 IOC 容器,管理的对象称之为 bean,Java 对象在 ClassLoader 中有自己的创建和清理过程,那么 Spring Bean 在容器中也有自己的生命周期。Spring Bean 的生命周期包括从诞生到销毁的整个过程,可以说,理解了 Spring Bean 的声明周期就理解了 Spring 容器对 bean 的管理。理解 Spring Bean 生命周期不仅便于我们在日常使用的对 Spring Bean 进行扩展,而且 Spring Bean 生命周期在面试中也是经常问到的一个考点。Spring Bean 生命周期各阶段遍布 BeanFactory 的各处,前面的文章中已经零零散散的对 Spring Bean 进行了介绍,下面围绕其生命周期进行分析。

Spring Bean 生命周期

Bean 元信息配置阶段

Spring 最早支持在 xml 中配置 Bean 的元信息,比较少用的还有 properties、groovy,这些 bean 元信息的配置都处于资源文件中,由于需要大量手工配置,因此目前使用比较多的是注解,配合类扫描使我们不必将配置元信息限制在某一文件中。

xml 文件配置 bean 元数据的示例如下。

<?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 class="com.zzuhkp.MyService"></bean>

</beans>

和 xml 配置等价的注解配置如下。

@Configuration
public class Config {

    @Bean
    public MyService myService() {
        return new MyService();
    }
}

Bean 元信息解析阶段

bean 配置元信息只是记录了 bean 的相关信息,创建 bean 时需要先转换为统一的表示形式,在 Spring 中使用 BeanDefinition 类表示,这和 Java 类在 JVM 中使用 Class 类表示是类似的。关于 BeanDefinition 更深入的理解可参见 《掌握 Spring 必须知道的 BeanDefinition》

对于 xml、properties、groovy 等保存 bean 元信息的文件,Spring 需要读取,对于注解的解析 Spring 同样需要读取 class 文件,因此 Spring 对资源进行了统一抽象,主要的类是 Resource 和 ResourceLoader,参见《Spring 资源管理 (Resource)》

对于保存 bean 元信息的资源文件,只是其文件内容不同,因此 Spring 进行了抽象,参见下面的类图。
Spring BeanDefinitionReaderAbstractBeanDefinitionReader 将各种资源转换为 Resource,其子类分别从 Resource 中解析 bean,XmlBeanDefinitionReader 用于解析 xml 文件、PropertiesBeanDefinitionReader 用于解析 properties 文件,GroovyBeanDefinitionReader 用于解析 groovy 文件,使用这种方式可以很方便的进行扩展。

对于注解 Spring 使用 AnnotatedBeanDefinitionReader 进行解析,和其他解析资源文件的 BeanDefinitionReader 不同,它并未实现任何接口,直接父类就是 Object ,这是因为它处理的元数据位于遍布于不同的 class 文件中。

通过 API 手动解析 bean 元信息的示例如下所示。

public class App {

    public static void main(String[] args) {
		BeanDefinitionRegistry beanDefinitionRegistry = new DefaultListableBeanFactory();
        
        // 解析 xml 中的 bean 元信息
        BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanDefinitionRegistry);
        beanDefinitionReader.loadBeanDefinitions("application.xml");

        // 解析注解中的 bean 元信息
        AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(beanDefinitionRegistry);
        annotatedBeanDefinitionReader.registerBean(Config.class);
    }
}

Bean 注册阶段

bean 注册是指将表示 bean 元信息的 BeanDefinition 保存在 spring 中,Spring 注册 BeanDefinition 的类为 BeanDefinitionRegistry ,使用 BeanDefinitionReader 加载或者注册 bean 的时候便会通过 BeanDefinitionRegistry#registerBeanDefinition 方法注册 BeanDefinition,Spring 内部 BeanDefinitionRegistry 的实现是 DefaultListableBeanFactory,使用了 一个 Map<String, BeanDefinition> 保存 BeanDefinition,其中键为 bean 的名称。

BeanDefinition 合并阶段

Spring 为了简化以及复用 xml 中的配置,为 bean 标签提供了一个 parent 的属性,可以用于指定 BeanDefinition 的父 BeanDefinition 的名称,这样就会继承其父 BeanDefinition 中的相关配置,并且子 BeanDefinition 可以覆盖父 BeanDefinition 中的相同配置,这和 Java 中的继承思想是相同的。AbstractBeanFactory#getMergedBeanDefinition 方法用于获取合并后的 BeanDefinition,使用 RootBeanDefinition 表示。

示例如下。

<?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 id="parentService" class="com.zzuhkp.ParentService">
        <property name="name" value="zzuhkp"/>
    </bean>
    
    <bean class="com.zzuhkp.MyService" parent="parentService">
        <property name="age" value="26"/>    
    </bean>

</beans>

配置后 MyService 将自动配置 ParentService 的 name 属性,注意 parent 并不要求一定是 bean 对应类的父类。

Bean 实例化阶段

Bean 的实例化阶段又分为实例化前、实例化中和实例化后三个小阶段,这是出于扩展的考虑,通过预留扩展点便于使用者自定义实例化的逻辑,具体如下。

Bean 实例化前阶段

Spring 预留 BeanPostProcessor 接口对初始化前和初始化后的 bean 进行处理,同时提供了 AbstractBeanFactory#addBeanPostProcessor 方法用于向 BeanFactory 中添加 BeanPostProcessor。

Spring 1.2 时,为了对 Bean 的实例化阶段进行扩展添加了继承 BeanPostProcessor 接口的 InstantiationAwareBeanPostProcessor 接口,在 bean 实例化前将调用 其#postProcessBeforeInstantiation 方法,方法定义如下。

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
	default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}
}

方法传入了 bean 对应的 Class 以及名称,返回了一个 Object 类型的对象,如果返回不为 null,则 表示使用返回的实例作为 bean,此时将忽略构造方法或者工厂方法创建 bean。

Bean 实例化中阶段

如果 Spring BeanFactory 中没有添加 InstantiationAwareBeanPostProcessor ,那么就会走正常的流程实例化 bean,此时需要根据参数解析出需要调用的构造方法或者工厂方法。不管是构造方法还是工厂方法最后都会委托到 InstantiationStrategy#instantiate,默认的 InstantiationStrategy 实现是 CglibSubclassingInstantiationStrategy,可用于创建 bean 时通过 Cglib 生成代理。实例化 bean 的具体代码,感兴趣的小伙伴可参见 AbstractAutowireCapableBeanFactory#createBeanInstance

Bean 实例化后阶段

与 bean 实例化前的阶段相对应,InstantiationAwareBeanPostProcessor 接口同样提供了 bean 实例化后进行回调的方法#postProcessAfterInstantiation,不管 Spring bean 是通过 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 方法进行实例化,还是通过正常的流程进行实例化,都会调用该接口,该接口定义如下。

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

	default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		return true;
	}
}

该方法接收 bean 对象和 bean 的名称,返回了一个 boolean ,如果为 false ,则显式设置的属性或者通过 autowrie 设置的自动装配都将忽略。利用该方法,可以在 bean 实例化后设置一些其他的属性值。

Bean 属性赋值阶段

Bean 属性赋值阶段是将显式设置的依赖或者自动注入的依赖设置到 bean 对象中。

Spring 在属性赋值前同样预留了扩展点,InstantiationAwareBeanPostProcessor 接口提供了方法#postProcessPropertyValues ,该方法在将属性赋值给 bean 对象之前调用,可用于再次手动设置属性,在 Spring 5.1 时被标记为废弃,与此同时提供了一个方法#postProcessProperties。这两个方法的定义如下。

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
	default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
			throws BeansException {

		return null;
	}

	default PropertyValues postProcessPropertyValues(
			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {

		return pvs;
	}
}

这两个方法都可以接收到表示 bean 属性的 PropertyValues ,#postProcessProperties 方法默认返回 null,此时将调用 #postProcessPropertyValues 方法,#postProcessPropertyValues 方法默认返回方法参数中的 PropertyValues 值,如果返回 null 将跳过属性的设置。

属性赋值前的回调之后随后就会通过反射正常的设置属性。

Bean 初始化阶段

Spring 初始化是指为了做初始化工作,调用某些初始化的方法。Spring 为 bean 的初始化同样预留了一些扩展点,这些扩展点分别代表着 bean 初始化的不同阶段。

Bean Aware 回调阶段

Spring BeanFactory 在 bean 初始化前首先会调用 Aware 相关的接口,代码位置为 AbstractAutowireCapableBeanFactory#invokeAwareMethods,对于 BeanFactory,会分别调用 BeanNameAware#setBeanNameBeanClassLoaderAware#setBeanClassLoader 以及 BeanFactoryAware#setBeanFactory 方法。

Bean 初始化前阶段

Bean 初始化前 Spring 将首先调用 BeanPostProcessor 的 postProcessBeforeInitialization 方法,方法定义如下。

public interface BeanPostProcessor {
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}

这里将 bean 的实例和名称作为参数,然后返回新的 bean 实例,如果返回的值不为 null,那么返回的值将作为 bean 的实例。

Bean 初始化阶段

Bean 初始化即调用初始化方法,初始化方法的来源有多种。

  1. @PostContruct:这个注解标注的方法将首先被调用,处理该注解的 BeanPostProcessor 为 CommonAnnotationBeanPostProcessor,实际使用了BeanPostProcessor #postProcessBeforeInitialization 回调方法来完成。
  2. InitializingBean#afterPropertiesSet:如果 bean 实现了接口 InitializingBean,随后 Spring 便会调用该接口的 afterPropertiesSet 方法。
  3. 自定义初始化方法:最后调用的是在 xml 或者注解中指定的初始化方法,代码位置为 AbstractAutowireCapableBeanFactory#invokeCustomInitMethod
Bean 初始化后阶段

bean 初始化后阶段与初始化前阶段相对应,Spring 初始化后将调用 BeanPostProcessor 接口的 postProcessAfterInitialization 方法,该方法的定义如下。

public interface BeanPostProcessor {

	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}

方法同样接收 bean 实例和 bean 名称,返回的为新使用的 bean 的名称。

Bean 初始化完成阶段

为了在所有的单例 bean 初始化后完成一些工作,Spring 提供了方法 SmartInitializingSingleton#afterSingletonsInstantiated,将我们的 bean 实现此接口即可,Spring 的事件处理 @EventListener 注解便是利用此回调实现。该方法的定义如下。

public interface SmartInitializingSingleton {
	void afterSingletonsInstantiated();
}

Bean 销毁阶段

通常情况我们不会手动对 bean 进行销毁,而是 Spring 应用上下文关闭之后才会 bean 进行自动销毁。Spring 将销毁阶段又划分为销毁前和销毁中阶段。

Bean 销毁前阶段

为了通知到用户 Spring 将对 bean 进行销毁,Spring 同样提供了扩展点,方法为DestructionAwareBeanPostProcessor#postProcessBeforeDestruction,该方法将在 Spring 指定销毁方法前调用,方法定义如下。

public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {

	void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;

}
Bean 销毁中阶段

这个阶段是 Spring 真正销毁 bean 的阶段,会调用 bean 的销毁方法。和初始化阶段相呼应,调用的销毁方法从前到后顺序分别如下。

  1. @PreDestroy :该注解同样为 CommonAnnotationBeanPostProcessor 处理,具体利用了 DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
  2. DisposableBean#destroy:如果 bean 实现了接口 DisposableBean,随后 Spring 会调用该方法执行销毁。
  3. 自定义销毁方法:如果在 xml 或者注解中指定了自定义的销毁方法将在最后进行调用。

总结

Spring Bean 生命周期贯穿 Spring 对 bean 的整个管理过程,首先将定义的元信息解析为 BeanDefinition 后注册,然后分别进行实例化、初始化和销毁,中间又涉及到各种小的阶段和 Spring 提供的回调方法。文章内容根据源码阅读获取,欢迎大家评论指导。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大鹏cool

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

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

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

打赏作者

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

抵扣说明:

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

余额充值