一个Bean的一生,究竟要走完多少路,才能消亡?

摘要:想了解 Spring 中 Bean 的销毁流程么?本文将从 Spring 源码的角度带你一步一步查看 Spring 中的 Bean 销毁时候生命周期的每个方法是如何被调用的。
生命周期流程 在前面的文章一个 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); } } }