Spring中Bean初始化与销毁实现

本文中源码来自Spring 5.3.x分支,github源码地址:github.com/spring-proj...

一 示例

Java 复制代码
package com.xiakexing.service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.stereotype.Component;

@Component
public class UserService {

    public void test() {
        System.out.println("创建了UserService");
    }

    @PostConstruct
    public void afterConstruct() {
        System.out.println("UserService PostConstruct");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("UserService destroy");
    }
}
Java 复制代码
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.test();
context.close();

@PostConstruct和@PreDestroy注解的方法,是如何被调用的呢?

二 @PostConstruct实现

在创建Bean的初始化前阶段,将执行BeanPostProcessor.postProcessBeforeInitialization()。在InitDestroyAnnotationBeanPostProcessor实现类,完成了对@PostConstruct的查找和回调。

2.1 InitDestroyAnnotationBeanPostProcessor

来看类注释

Allows for an annotation alternative to Spring's org.springframework.beans.factory.InitializingBean and org.springframework.beans.factory.DisposableBean callback interfaces.

允许一个注释替代Spring的org.springframework.beans.factory.InitializingBean和org.springframework.beans.factory.DisposableBean回调接口。

Init and destroy annotations may be applied to methods of any visibility: public, package-protected, protected, or private. Multiple such methods may be annotated, but it is recommended to only annotate one single init method and destroy method, respectively.

Init和destroy注解可以应用于任何可见性的方法:public、package-protected、protected或private。可以注释多个这样的方法,但建议分别只注释一个init方法和destroy方法。

有以下结论:

  1. @PostConstruct是为代替InitializingBean接口,@PreDestroy是为代替DisposableBean接口;
  2. 这两个注解可修饰带有任何权限修饰符的方法;
  3. 在一个类中,可以多次使用这两个注解;但建议只注释一个init方法和destroy方法。

2.2 收集生命周期方法

首先在class中查找init和destory方法,封装为LifecycleElement对象,为每个Class创建一个LifecycleMetadata对象。 LifecycleMetadata一经构建,就放入lifecycleMetadataCache中缓存。

initAnnotationType其实就是PostConstruct.class,destroyAnnotationType就是PreDestroy.class。这两属性是在子类CommonAnnotationBeanPostProcessor中赋值的。

ReflectionUtils#doWithLocalMethods中,会遍历类中每个方法,执行MethodCallback.doWith。(函数式接口,类似于Consumer)

2.3 执行初始化方法

遍历initMethods,反射调用。

三 Bean的销毁

关闭Spring容器,通常有两种方式:

  1. 主动调用ApplicationContext.close();
  2. 或者ConfigurableApplicationContext.registerShutdownHook(),注册钩子,在进程结束时将关闭Spring容器。

在Spring容器将关闭前,想要对bean执行自定义的销毁动作。通常有以下实现方式:

  1. 实现DisposableBean接口,复写destroy();
  2. 实现AutoCloseable接口,复写close();
  3. BeanDefinition中指定了destroyMethod,如通过@Bean(destroyMethod="XXX");
  4. 使用@PreDestroy标注bean的销毁方法。而该注解就是为了代替DisposableBean接口。

在Bean被创建的最后一步,会将定义了销毁逻辑的Bean,注册到disposableBeans缓存中。

3.1 适配器模式

如果一个Bean实现了DisposableBean接口,或者实现了AutoCloseable接口,或者在BeanDefinition中指定了destroyMethod,那么这个Bean都属于"DisposableBean",在容器关闭时都要调用相应的销毁方法。

为了将各个销毁方式,统一适配成DisposableBean实现,就用到DisposableBeanAdapter。

3.2 Bean的销毁

关闭Spring容器时,会调用AbstractApplicationContext#doClose,有如下流程:

  1. 首先发布ContextClosedEvent事件;
  2. 调用lifecycleProcessor的onCloese()方法;
  3. 销毁单例Bean;
  4. 调用closeBeanFactory,关闭容器;
  5. 执行AbstractApplicationContext#onClose模板方法;

可见,此时会触发对单例Bean的销毁。在DefaultSingletonBeanRegistry#destroySingletons中,将遍历disposableBeans缓存:

  1. 把每个disposableBean从singletonObjects、singletonFactories中移除
  2. 如果这个disposableBean被其他Bean依赖了,那么先销毁依赖Bean
  3. 调用disposableBean的destroy(),销毁当前disposableBean;
  4. 如果当前disposableBean还包含了inner beans,将这些Bean也销毁;
  5. 清空各种缓存Map,包括singletonObjects、dependentBeanMap等。

3.3 @PreDestroy实现

DestructionAwareBeanPostProcessor接口,在Bean对象销毁之前,可执行自定义逻辑。

  • postProcessBeforeDestruction:Bean销毁前的自定义动作;
  • requiresDestruction:确定给定的bean实例是否需要执行这个后处理器,默认返回true。

当容器中有DestructionAwareBeanPostProcessor对象,且requiresDestruction方法对某个Bean返回true时,这个Bean也会被注册到disposableBeans缓存中。

当容器关闭时,对某个Bean执行DisposableBean.destroy()前,会先执行DestructionAwareBeanPostProcessor.postProcessBeforeDestruction方法。

源码中有个默认实现InitDestroyAnnotationBeanPostProcessor,完成了对@PreDestroy方法的回调。

相关推荐
卷到起飞的数分几秒前
Java零基础笔记07(Java编程核心:面向对象编程 {类,static关键字})
java·开发语言·笔记
舌尖上的五香9 分钟前
ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal
java
okok__TXF10 分钟前
Sentinel入门篇【流量治理】
java·sentinel
谁他个天昏地暗12 分钟前
Java 实现 Excel 文件对比与数据填充
java·开发语言·excel
今天背单词了吗98035 分钟前
算法学习笔记:11.冒泡排序——从原理到实战,涵盖 LeetCode 与考研 408 例题
java·学习·算法·排序算法·冒泡排序
Brookty42 分钟前
【操作系统】进程(二)内存管理、通信
java·linux·服务器·网络·学习·java-ee·操作系统
风象南43 分钟前
SpringBoot 与 HTMX:现代 Web 开发的高效组合
java·spring boot·后端
倔强的小石头_4 小时前
【C语言指南】函数指针深度解析
java·c语言·算法
Jay Kay5 小时前
TensorFlow内核剖析:分布式TensorFlow架构解析与实战指南
分布式·架构·tensorflow
亿牛云爬虫专家7 小时前
Kubernetes下的分布式采集系统设计与实战:趋势监测失效引发的架构进化
分布式·python·架构·kubernetes·爬虫代理·监测·采集