生命周期流程
在前面的文章一个 Bean 就这样走完了它的一生之 Bean 的出生里面讲解了 Spring 中 Bean 的创建的生命周期,这篇文章再来看一下 Bean 销毁的生命周期。在 Spring 容器被关闭的时候,会调用依次调用 Bean 销毁的生命周期方法,如下图所示:
案例解析
数据库连接池在 Bean 销毁时主动关闭连接
假设现在定义了一个数据库连接池相关的 Bean,使用了 @PreDestroy
注解修饰了它的 shutdown()
方法,当 Spring 容器销毁的时候,它的 shutdown()
方法就会调用,以便实现在程序终止的时候关闭和数据库创建的连接。代码如下:
@Component
public class DatabaseConnectionPool {
private final String url = "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1";
private final String username = "sa";
private final String password = "";
private final int initialPoolSize = 5;
private Queue<Connection> connectionPool;
private List<Connection> activeConnections;
public DatabaseConnectionPool() {
this.connectionPool = new ConcurrentLinkedQueue<>();
this.activeConnections = new ArrayList<>();
}
@PostConstruct
public void init() {
//初始化连接
}
@PreDestroy
public void shutdown() {
for (Connection conn : activeConnections) {
try {
if (conn != null && !conn.isClosed()) {
conn.close();
System.out.println("DatabaseConnectionPool: 关闭连接: " + conn);
}
} catch (SQLException e) {
System.err.println("DatabaseConnectionPool: 关闭连接失败: " + e.getMessage());
}
}
connectionPool.clear();
activeConnections.clear();
}
}
Bean 销毁源码解析
DisposableBean 的注册
在 Bean 创建的过程中,在 AbstractAutowireCapableBeanFactory
的 doCreateBean()
方法中会判断一个 Bean 是否应该在容器关闭的时候被销毁,如果需要的话就会为当前 Bean 注册一个 DisposableBeanAdapter
的对象。代码如下:
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
//这里判断
if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
if (mbd.isSingleton()) {
//这里创建了一个 DisposableBeanAdapter 对象
registerDisposableBean(beanName, new DisposableBeanAdapter(
bean, beanName, mbd, getBeanPostProcessorCache().destructionAware));
}
else {
Scope scope = this.scopes.get(mbd.getScope());
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
}
scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(
bean, beanName, mbd, getBeanPostProcessorCache().destructionAware));
}
}
}
在判断的时候首先会判断 Bean 是否有销毁方法。主要是在 hasDestroyMethod()
方法中实现的。在该方法中判断一个 Bean 是否实现了 DisposableBean
接口或者是否有定义销毁方法,而判断是否有销毁方法则是通过判断 Bean 定义中的 destroyMethodNames
属性不为空来实现的。这个属性的值就是解析 @Bean
注解的 destroyMethod
属性配置的方法或者XML中的destroy-method
配置的方法。代码如下:
protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
return (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) ||
(hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(
bean, getBeanPostProcessorCache().destructionAware))));
}
public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
//这里判断是否实现了DisposableBean接口
return (bean instanceof DisposableBean ||
inferDestroyMethodsIfNecessary(bean.getClass(), beanDefinition) != null);
}
static String[] inferDestroyMethodsIfNecessary(Class<?> target, RootBeanDefinition beanDefinition) {
//这里判断Bean定义的destroyMethodNames是否不为空,而destroyMethodNames就是
//解析@Bean的destroyMethod属性配置的方法或者XML中的destroy-method配置的方法
String[] destroyMethodNames = beanDefinition.getDestroyMethodNames();
if (destroyMethodNames != null && destroyMethodNames.length > 1) {
return destroyMethodNames;
}
//省略代码
}
然后就会判断是否有 DestructionAwareBeanPostProcessor
,如果有的话,则会调用它的 requiresDestruction()
进行判断。而 @PreDestroy
注解修饰的方法就是由 InitDestroyAnnotationBeanPostProcessor
来进行真实的判断的,而它的父类是我们的老朋友 CommonAnnotationBeanPostProcessor
,在它的构造函数中初始化了要处理 @PreDestroy
注解。代码如下:
class DisposableBeanAdapter {
public static boolean hasApplicableProcessors(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors) {
if (!CollectionUtils.isEmpty(postProcessors)) {
for (DestructionAwareBeanPostProcessor processor : postProcessors) {
//这里调用requiresDestruction()方法进行判断
if (processor.requiresDestruction(bean)) {
return true;
}
}
}
return false;
}
}
public class InitDestroyAnnotationBeanPostProcessor {
public boolean requiresDestruction(Object bean) {
return findLifecycleMetadata(bean.getClass()).hasDestroyMethods();
}
private LifecycleMetadata findLifecycleMetadata(Class<?> beanClass) {
if (this.lifecycleMetadataCache == null) {
// Happens after deserialization, during destruction...
return buildLifecycleMetadata(beanClass);
}
// Quick check on the concurrent map first, with minimal locking.
LifecycleMetadata metadata = this.lifecycleMetadataCache.get(beanClass);
if (metadata == null) {
synchronized (this.lifecycleMetadataCache) {
metadata = this.lifecycleMetadataCache.get(beanClass);
if (metadata == null) {
metadata = buildLifecycleMetadata(beanClass);
this.lifecycleMetadataCache.put(beanClass, metadata);
}
return metadata;
}
}
return metadata;
}
}
public class CommonAnnotationBeanPostProcessor {
public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
// Jakarta EE 9 set of annotations in jakarta.annotation package
addInitAnnotationType(loadAnnotationType("jakarta.annotation.PostConstruct"));
addDestroyAnnotationType(loadAnnotationType("jakarta.annotation.PreDestroy"));
// Tolerate legacy JSR-250 annotations in javax.annotation package
addInitAnnotationType(loadAnnotationType("javax.annotation.PostConstruct"));
addDestroyAnnotationType(loadAnnotationType("javax.annotation.PreDestroy"));
// java.naming module present on JDK 9+?
if (jndiPresent) {
this.jndiFactory = new SimpleJndiBeanFactory();
}
}
}
JVM 虚拟机 ShutdownHook 注册
在 Spring 的 AbstractApplicationContext
中提供了一个 registerShutdownHook()
的方法,它可以向 JVM 注册一个钩子线程,在 JVM 进程终止的时候启动这个线程,完成对容器的销毁操作。在我们的业务代码中可以手动调用这个方法注册。代码如下:
public class AbstractApplicationContext {
public void registerShutdownHook() {
if (this.shutdownHook == null) {
// No shutdown hook registered yet.
this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
@Override
public void run() {
if (isStartupShutdownThreadStuck()) {
active.set(false);
return;
}
startupShutdownLock.lock();
try {
doClose();
}
finally {
startupShutdownLock.unlock();
}
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
}
public clas SpringTest {
public static void main(String[] args) {
// 1. 创建并初始化 Spring 容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 2. 注册 JVM 关闭Hook
// 这一步是显式调用的,由开发者决定何时执行
context.registerShutdownHook();
}
}
而在 Spring Boot 项目中则提供了 SpringApplicationShutdownHook
回调类,在 Spring Boot 项目启动时,通过判断 spring.main.register-shutdown-hook
来决定是否注册回调。代码如下:
public class SpringApplication {
public ConfigurableApplicationContext run(String... args) {
Startup startup = Startup.create();
if (this.properties.isRegisterShutdownHook()) {
SpringApplication.shutdownHook.enableShutdownHookAddition();
}
}
private void refreshContext(ConfigurableApplicationContext context) {
if (this.properties.isRegisterShutdownHook()) {
shutdownHook.registerApplicationContext(context);
}
refresh(context);
}
}
class SpringApplicationShutdownHook implements Runnable {
void registerApplicationContext(ConfigurableApplicationContext context) {
addRuntimeShutdownHookIfNecessary();
synchronized (SpringApplicationShutdownHook.class) {
assertNotInProgress();
context.addApplicationListener(this.contextCloseListener);
this.contexts.add(context);
}
}
private void addRuntimeShutdownHookIfNecessary() {
if (this.shutdownHookAdditionEnabled && this.shutdownHookAdded.compareAndSet(false, true)) {
addRuntimeShutdownHook();
}
}
void addRuntimeShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(this, "SpringApplicationShutdownHook"));
}
@Override
public void run() {
Set<ConfigurableApplicationContext> contexts;
Set<ConfigurableApplicationContext> closedContexts;
List<Handler> handlers;
synchronized (SpringApplicationShutdownHook.class) {
this.inProgress = true;
contexts = new LinkedHashSet<>(this.contexts);
closedContexts = new LinkedHashSet<>(this.closedContexts);
handlers = new ArrayList<>(this.handlers.getActions());
Collections.reverse(handlers);
}
//这里调用了容器的closeAndWait方法
contexts.forEach(this::closeAndWait);
closedContexts.forEach(this::closeAndWait);
handlers.forEach(Handler::run);
}
}
Bean 销毁方法调用
注册的回调线程启动的时候会调用 AbstractApplicationContext
的 doClose()
方法,然后调用 destroyBeans()
方法,然后会调用到 DefaultSingletonBeanRegistry
的 destroySingletons()
方法。代码如下:
public abstract class AbstractApplicationContext {
protected void doClose() {
// Check whether an actual close attempt is necessary...
if (this.active.get() && this.closed.compareAndSet(false, true)) {
//省略代码
// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();
}
protected void destroyBeans() {
getBeanFactory().destroySingletons();
}
}
public class DefaultSingletonBeanRegistry {
public void destroySingletons() {
this.singletonsCurrentlyInDestruction = true;
String[] disposableBeanNames;
synchronized (this.disposableBeans) {
disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
}
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
destroySingleton(disposableBeanNames[i]);
}
this.containedBeanMap.clear();
this.dependentBeanMap.clear();
this.dependenciesForBeanMap.clear();
this.singletonLock.lock();
try {
clearSingletonCache();
}
finally {
this.singletonLock.unlock();
}
}
}
在 DefaultSingletonBeanRegistry
的 destroySingletons()
方法会循环调用之前注册的 DisposableBeanAdapter
的 destroy()
方法,在该方法中实现了对销毁生命周期方法的调用。这里面首先调用的是 DestructionAwareBeanPostProcessor
的 postProcessBeforeDestruction()
方法,在该方法中实现了对 @PreDestroy
注解修饰的方法的调用,然后调用 DisposableBean
的 destroy()
方法,最后再调用自定义的销毁方法。代码如下:
public void destroy() {
if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
//调用DestructionAwareBeanPostProcessor的postProcessBeforeDestruction()方法
processor.postProcessBeforeDestruction(this.bean, this.beanName);
}
}
if (this.invokeDisposableBean) {
try {
//调用DisposableBean的destroy()方法
((DisposableBean) this.bean).destroy();
}
catch (Throwable ex) {
}
} else if (this.destroyMethods != null) {
for (Method destroyMethod : this.destroyMethods) {
//调用自定义初始化方法
invokeCustomDestroyMethod(destroyMethod);
}
}
}