SpringFramework — Bean完整的生命周期概述

小龙 525 2022-05-23

前言

之前理解的 bean 的生命周期大概是这样的:

image-1653296321966

如果这是一个普通的 Java 对象,那么图中所属的生命周期完全合理。但是在 SpringFramework 中,这个流程就不够完整了。复习一下 前面学习过的内容

bean的创建之前

在 Bean 创建之间,Bean 是以 BeanDefinition 的方式被描述和注册在 BeanFactory 的(准确的说是注册在 BeanDefinitionRegistry 中),这个时候 Bean 还未被创建,但是 BeanFactory 中已经存在 bean 对应的 BeanDefinition 了,所以在 bean 的实例化之前,还应该有 BeanDefinition 阶段

BeanDefinition的由来

BeanDefinition 也不是凭空出现的,要么是解析了配置文件,要么是解析了配置类,要么有人编程式的注入了 BeanDefinition ,所以如果纵观整个 bean 的生命周期,在 BeanDefinition 阶段之前,还应该有 BeanDefinition 的来源和加载阶段。

结合上面的理解,我们以 【单实例 bean 】 的全生命周期为主线讨论,具体包含以下部分:

  • BeanDefinition 部分
    • BeanDefinition 的解析
    • BeanDefinition 的注册
  • bean 实例部分
    • bean 的实例化
    • bean 的属性赋值 + 依赖注入
    • bean 的初始化流程
    • bean 的启动与停止
    • bean 的销毁

BeanDefinition阶段的主要工作

BeanDefinition 的来源

问题:bean 的实例化需要先有 BeanDefinition 的信息,所以 BeanDefinition 都是怎么来的呢?

一、声明式 @Configuration + @Bean

@Configuration
public class beanConfig{
	@Bean
	public Person person(){
    	return new Person();
    }
}

二、@Component + @ComponentScan

@Component
@ComponentScan("com.rsthe")
public class ComponentScanConfiguration{
}

三、@Import

@Import({"Boos.class, Red.class"})
public @interface Enabletavern

四、配置式注入 BeanDefinition

<bean> 标签声明

<bean id="person" class="com.rsthe.bean.Person"/>

编程时注入 BeanDefinition

  • ImportBeanDefinitionRegistrar
public class WaiterRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        registry.registerBeanDefinition("waiter", new RootBeanDefinition(Waiter.class));
    }
}
  • 手动构造 BeanDefinition 注入
 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();

    BeanDefinition personDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class)
            .addPropertyValue("name", "zhangsan").getBeanDefinition();
    ctx.registerBeanDefinition("person", personDefinition);

    ctx.refresh();
  • 借助 BeanDefinitionRegistryPostProcessor 注入
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    registry.registerBeanDefinition("animalNameSetterPostProcessor", 
            new RootBeanDefinition(AnimalNameSetterPostProcessor.class));
}

通过上述的方式,就可以把 BeanDefinition 注入到 BeanFactory 中了。

BeanDefinition的后置处理

BeanDefinition 都构建好了之后,是不会理解注册到 BeanFactory 的,在这中间还有一步执行 BeanDefinitionRegistryPostProcessor 动作,等这些 BeanDefinitionRegistryPostProcessor 都执行完 postProcessorBeanDefinitionRegistry 方法之后,BeanDefinition 才会注册到 BeanFactory

image-1653300279519

这个动作完成之后,下一步就是执行 BeanFactoryPostProcessorpostProcessorBeanFactory 方法了。这个阶段可以修改 BeanDefinition 的配置信息,添加注入的依赖项,给属性赋值等操作

image-1653300395901

这一步执行完毕后,BeanDefinition 就不会再动了,BeanDefinition 的阶段也就算结束了。

bean实例阶段的内容

AbstractApplicationContextrefresh() 找到执行步骤,其中第11步,finishBeanFactoryInitialocation() 方法就是初始化 Bean 实例的动作了:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        
        try {
            postProcessBeanFactory(beanFactory);
            invokeBeanFactoryPostProcessors(beanFactory);
            registerBeanPostProcessors(beanFactory);
            initMessageSource();
            initApplicationEventMulticaster();
            onRefresh();
            registerListeners();
            // 11. 初始化剩余的单实例bean
            finishBeanFactoryInitialization(beanFactory);
            finishRefresh();
        }
        // catch ......
        finally {
            resetCommonCaches();
        }
    }
}

这一步的动作非常的多,它会将所有还未实例化的、非延迟加载的单例 bean 都创建出来。而且创建一个 bean 的流程非常的多且复杂。大致可以分为 5 个部分:

  • BeanDefinition 的合并
  • bean 的实例化
  • 属性赋值 + 依赖注入
  • bean 的初始化
  • bean 的启动

BeanDefinition 的合并

bean 的实例化阶段第一步并不是直接创建对象。BeanDefinition 有一个合并的特性,如果一个 <bean> 有继承另一个 <bean> 定义,则会形成父子定义。这种情况下,创建 bean 的时候就需要读取父 bean 的定义,合并为最终的 RootBeanDefinition。

Bean 的实例化

并完 BeanDefinition ,下一步才是真正的实例化 bean ,不过这里要注意一个问题:如果 bean 是一个 FactoryBean ,这里该如何处理呢?是只创建 FactoryBean 本身?还是连 FactoryBean 创建的真实对象一起创建?

除了考虑 FactoryBean 之外,还有一个要考虑的因素是缓存问题。咱在一开始学 IOC 的由来时,就知道 BeanFactory 中可以缓存单实例 bean ,那么在 Spring 中是如何处理单实例 bean 在实例化后的缓存呢?

属性赋值+依赖注入

bean 对象实例化完成后,里面所有的成员都是空的,接下来的步骤就是属性赋值和依赖注入了。由于这部分逻辑属于 bean 实例已经创建,所以将该步骤划到下面的 bean 初始化部分。

bean的初始化

可以执行 initMethod 指定的方法、@postConstruct 注解标注的类、InitializingBean接口的方法。
执行优先级:@postConstruct -> InitializingBean接口 -> initMethod

bean的启动

在 bean 的生命周期中还有一个接口:Lifecycle ,这个接口的执行是依照 的执行是依照 ApplicationContext 的生命周期而来 的生命周期而来,两者是相互关联的。

Lifecycle 的触发时机:在 refresh 方法的 try 块最后一行:finishRefresh ,它在里面会找出所有实现了 Lifecycle 接口的 bean ,并调用它们的 start 方法。注意此时所有实现了 Lifecycle 的非延迟加载的单实例 bean 都已经加载完成了,是可以正常调用 start 方法的。

一般实现 Lifecycle 的 start 方法多用于建立连接加载资源等等操作,以备程序的运行期使用。

这个阶段 ApplicationContext 就会完成所有 Lifecycle 类型的 bean 的 start 方法调用,对于一个 bean 的生命周期而言,就已经到了正常存活期了。

bean销毁阶段的内容

当 ApplicationContext 关闭时,这些 bean 也是需要销毁的,相比较于 bean 的创建和初始化,销毁部分就变得比较简单了,我们可以先对整个 ApplicationContext 的关闭有一个整体的了解。

关闭 ApplicationContext 会顺序执行下面几个步骤:

  1. 广播容器关闭时间
    • ContextClosedEvent 时间
  2. 通知所有实现了 Lifecycle 的 bean 回调 close() 方法
  3. 销毁所有 bean
  4. 关闭 BeanFactory
  5. 标记本身为不可用

上面这个五个步骤中,关于 Bean 销毁的只有2、3步。

bean的停止

与 bean 启动一致,如果一个 bean 实现了 Lifecycle 接口,此处会回调 bean 的 close 方法。由于此时 bean 的销毁回调方法还没有执行,所以在销毁阶段,Lifecycle 的执行时机是最靠前的

一般实现 Lifecycle 的 close 方法多用来关闭连接释放资源等操作,因为程序终止了,这些资源也就没必要持有了。

bean的销毁回调

bean “停止” 后,接下来就是对应于 bean 初始化阶段的三种生命周期回调的销毁部分了。不过这里面有一个不同点:BeanPostProcessor 的回调包含 bean 的初始化之前和初始化之后,但 DestructionAwareBeanPostProcessor包含 bean 销毁回调之前的动作,没有之后

完整的 Bean 生命周期

================准备刷新IOC容器==================
LifecyclePersonBeanFactoryPostProcessor(BeanFactoryPostProcessor) ---> com.rsthe.springBeanFullLifeCycle.config.PersonConfiguration$$EnhancerBySpringCGLIB$$2f3f95dc
Person 构造方法执行。。。
Person BeanNameAware 接口 setBeanName() 方法执行
Person @PostConstruct 注解标注方法执行。。。
Person InitializingBean 接口 afterPropertiesSet() 方法执行。。。
Person initMethod() 方法执行。。。
postProcessAfterInitialization ---> 小明
================IOC容器刷新完毕==================
Person 睡醒起床了 ......
================IOC容器启动完成==================
Person{name='小明', state=true}
Dog{person=Person{name='小明', state=true}, name='旺财'}
================准备停止IOC容器==================
Person 睡觉去了 ......
================IOC容器停止成功==================
LifecycleDestructionPostProcessor --->  旺财 跑走了
Person @PreDestroy 注解标注方法执行。。。
Person DisposableBean 接口 destroy() 方法执行。。。
Person destroyMethod() 方法执行。。。

ApplicationConext IOC 容器创建之后,并不会自动的去加载 Bean 配置,需要使用 refresh() 方法刷新容器。刷新容器后就有完整的 Bean 生命周期过程

IOC 容器刷新阶段

  1. 执行 BeanFactoryPostProcessor 接口
  2. 执行 class 的构造方法
  3. 执行各种 Aware 接口的回调方法
  4. 执行 @PostConstruct 注解标注方法
  5. 执行 InitializingBean 接口 afterPropertiesSet() 方法
  6. 执行 initMethod() 方法
  7. 执行 BeanPostProcessor 接口方法

至此 IOC 容器刷新完成,bean 实例创建;接下来就是 IOC 容器启动完成之前操作

  1. 执行 Lifecycle 接口的 start() 方法

第 8 步执行完 ,IOC 容器启动完成,进入使用阶段

  1. bean 使用阶段

第 9 步执行完, IOC 容器进入准备停止阶段

  1. 执行 Lifecycle 接口的 stop() 方法

第 10 步执行完成,IOC 容器停止成功

  1. 执行 DestructionAwareBeanPostProcessor 接口的方法
  2. 执行 @PreDestroy 注解标注方法
  3. 执行 DisposableBean 接口 destroy() 方法
  4. 执行 destroyMethod() 方法

至此 Bean 的生命周期完结


# IOC