本文中源码来自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方法。
有以下结论:
- @PostConstruct是为代替InitializingBean接口,@PreDestroy是为代替DisposableBean接口;
- 这两个注解可修饰带有任何权限修饰符的方法;
- 在一个类中,可以多次使用这两个注解;但建议只注释一个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容器,通常有两种方式:
- 主动调用ApplicationContext.close();
- 或者ConfigurableApplicationContext.registerShutdownHook(),注册钩子,在进程结束时将关闭Spring容器。
在Spring容器将关闭前,想要对bean执行自定义的销毁动作。通常有以下实现方式:
- 实现DisposableBean接口,复写destroy();
- 实现AutoCloseable接口,复写close();
- BeanDefinition中指定了destroyMethod,如通过@Bean(destroyMethod="XXX");
- 使用@PreDestroy标注bean的销毁方法。而该注解就是为了代替DisposableBean接口。
在Bean被创建的最后一步,会将定义了销毁逻辑的Bean,注册到disposableBeans缓存中。
3.1 适配器模式
如果一个Bean实现了DisposableBean接口,或者实现了AutoCloseable接口,或者在BeanDefinition中指定了destroyMethod,那么这个Bean都属于"DisposableBean",在容器关闭时都要调用相应的销毁方法。
为了将各个销毁方式,统一适配成DisposableBean实现,就用到DisposableBeanAdapter。
3.2 Bean的销毁
关闭Spring容器时,会调用AbstractApplicationContext#doClose,有如下流程:
- 首先发布ContextClosedEvent事件;
- 调用lifecycleProcessor的onCloese()方法;
- 销毁单例Bean;
- 调用closeBeanFactory,关闭容器;
- 执行AbstractApplicationContext#onClose模板方法;
可见,此时会触发对单例Bean的销毁。在DefaultSingletonBeanRegistry#destroySingletons中,将遍历disposableBeans缓存:
- 把每个disposableBean从singletonObjects、singletonFactories中移除
- 如果这个disposableBean被其他Bean依赖了,那么先销毁依赖Bean
- 调用disposableBean的destroy(),销毁当前disposableBean;
- 如果当前disposableBean还包含了inner beans,将这些Bean也销毁;
- 清空各种缓存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方法的回调。