找回密码
 立即注册
首页 业界区 安全 一个 Bean 就这样走完了它的一生之 Bean 的消亡 ...

一个 Bean 就这样走完了它的一生之 Bean 的消亡

舒娅友 2025-5-30 13:34:12
生命周期流程

在前面的文章一个 Bean 就这样走完了它的一生之 Bean 的出生里面讲解了 Spring 中 Bean 的创建的生命周期,这篇文章再来看一下 Bean 销毁的生命周期。在 Spring 容器被关闭的时候,会调用依次调用 Bean 销毁的生命周期方法,如下图所示:
1.png

案例解析

数据库连接池在 Bean 销毁时主动关闭连接

假设现在定义了一个数据库连接池相关的 Bean,使用了 @PreDestroy 注解修饰了它的 shutdown() 方法,当 Spring 容器销毁的时候,它的 shutdown() 方法就会调用,以便实现在程序终止的时候关闭和数据库创建的连接。代码如下:
  1. @Component
  2. public class DatabaseConnectionPool {
  3.     private final String url = "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1";
  4.     private final String username = "sa";
  5.     private final String password = "";
  6.     private final int initialPoolSize = 5;
  7.     private Queue<Connection> connectionPool;
  8.     private List<Connection> activeConnections;
  9.     public DatabaseConnectionPool() {
  10.         this.connectionPool = new ConcurrentLinkedQueue<>();
  11.         this.activeConnections = new ArrayList<>();
  12.     }
  13.     @PostConstruct
  14.     public void init() {
  15.        //初始化连接
  16.     }
  17.     @PreDestroy
  18.     public void shutdown() {
  19.         for (Connection conn : activeConnections) {
  20.             try {
  21.                 if (conn != null && !conn.isClosed()) {
  22.                     conn.close();
  23.                     System.out.println("DatabaseConnectionPool: 关闭连接: " + conn);
  24.                 }
  25.             } catch (SQLException e) {
  26.                 System.err.println("DatabaseConnectionPool: 关闭连接失败: " + e.getMessage());
  27.             }
  28.         }
  29.         connectionPool.clear();
  30.         activeConnections.clear();
  31.     }
  32. }
复制代码
Bean 销毁源码解析

DisposableBean 的注册

在 Bean 创建的过程中,在 AbstractAutowireCapableBeanFactory 的 doCreateBean() 方法中会判断一个 Bean 是否应该在容器关闭的时候被销毁,如果需要的话就会为当前 Bean 注册一个 DisposableBeanAdapter的对象。代码如下:
  1. protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
  2.         //这里判断
  3.         if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
  4.             if (mbd.isSingleton()) {
  5.                 //这里创建了一个 DisposableBeanAdapter 对象
  6.                 registerDisposableBean(beanName, new DisposableBeanAdapter(
  7.                         bean, beanName, mbd, getBeanPostProcessorCache().destructionAware));
  8.             }
  9.             else {
  10.                 Scope scope = this.scopes.get(mbd.getScope());
  11.                 if (scope == null) {
  12.                     throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
  13.                 }
  14.                 scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(
  15.                         bean, beanName, mbd, getBeanPostProcessorCache().destructionAware));
  16.             }
  17.         }
  18.     }
复制代码
在判断的时候首先会判断 Bean 是否有销毁方法。主要是在 hasDestroyMethod() 方法中实现的。在该方法中判断一个 Bean 是否实现了 DisposableBean 接口或者是否有定义销毁方法,而判断是否有销毁方法则是通过判断 Bean 定义中的 destroyMethodNames 属性不为空来实现的。这个属性的值就是解析 @Bean 注解的 destroyMethod 属性配置的方法或者XML中的destroy-method 配置的方法。代码如下:
  1. protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
  2.     return (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) ||
  3.             (hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(
  4.                     bean, getBeanPostProcessorCache().destructionAware))));
  5. }
  6. public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
  7.     //这里判断是否实现了DisposableBean接口
  8.     return (bean instanceof DisposableBean ||
  9.             inferDestroyMethodsIfNecessary(bean.getClass(), beanDefinition) != null);
  10. }
  11.    
  12. static String[] inferDestroyMethodsIfNecessary(Class<?> target, RootBeanDefinition beanDefinition) {
  13.     //这里判断Bean定义的destroyMethodNames是否不为空,而destroyMethodNames就是
  14.     //解析@Bean的destroyMethod属性配置的方法或者XML中的destroy-method配置的方法
  15.     String[] destroyMethodNames = beanDefinition.getDestroyMethodNames();
  16.     if (destroyMethodNames != null && destroyMethodNames.length > 1) {
  17.         return destroyMethodNames;
  18.     }
  19.     //省略代码
  20. }
复制代码
然后就会判断是否有 DestructionAwareBeanPostProcessor,如果有的话,则会调用它的 requiresDestruction() 进行判断。而 @PreDestroy 注解修饰的方法就是由 InitDestroyAnnotationBeanPostProcessor 来进行真实的判断的,而它的父类是我们的老朋友 CommonAnnotationBeanPostProcessor,在它的构造函数中初始化了要处理 @PreDestroy 注解。代码如下:
  1. class DisposableBeanAdapter {
  2.     public static boolean hasApplicableProcessors(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors) {
  3.         if (!CollectionUtils.isEmpty(postProcessors)) {
  4.             for (DestructionAwareBeanPostProcessor processor : postProcessors) {
  5.                 //这里调用requiresDestruction()方法进行判断
  6.                 if (processor.requiresDestruction(bean)) {
  7.                     return true;
  8.                 }
  9.             }
  10.         }
  11.         return false;
  12.     }
  13. }
  14. public class InitDestroyAnnotationBeanPostProcessor {
  15.     public boolean requiresDestruction(Object bean) {
  16.         return findLifecycleMetadata(bean.getClass()).hasDestroyMethods();
  17.     }
  18.     private LifecycleMetadata findLifecycleMetadata(Class<?> beanClass) {
  19.         if (this.lifecycleMetadataCache == null) {
  20.             // Happens after deserialization, during destruction...
  21.             return buildLifecycleMetadata(beanClass);
  22.         }
  23.         // Quick check on the concurrent map first, with minimal locking.
  24.         LifecycleMetadata metadata = this.lifecycleMetadataCache.get(beanClass);
  25.         if (metadata == null) {
  26.             synchronized (this.lifecycleMetadataCache) {
  27.                 metadata = this.lifecycleMetadataCache.get(beanClass);
  28.                 if (metadata == null) {
  29.                     metadata = buildLifecycleMetadata(beanClass);
  30.                     this.lifecycleMetadataCache.put(beanClass, metadata);
  31.                 }
  32.                 return metadata;
  33.             }
  34.         }
  35.         return metadata;
  36.     }
  37. }
  38. public class CommonAnnotationBeanPostProcessor {
  39.     public CommonAnnotationBeanPostProcessor() {
  40.         setOrder(Ordered.LOWEST_PRECEDENCE - 3);
  41.         // Jakarta EE 9 set of annotations in jakarta.annotation package
  42.         addInitAnnotationType(loadAnnotationType("jakarta.annotation.PostConstruct"));
  43.         addDestroyAnnotationType(loadAnnotationType("jakarta.annotation.PreDestroy"));
  44.         // Tolerate legacy JSR-250 annotations in javax.annotation package
  45.         addInitAnnotationType(loadAnnotationType("javax.annotation.PostConstruct"));
  46.         addDestroyAnnotationType(loadAnnotationType("javax.annotation.PreDestroy"));
  47.         // java.naming module present on JDK 9+?
  48.         if (jndiPresent) {
  49.             this.jndiFactory = new SimpleJndiBeanFactory();
  50.         }
  51.     }
  52. }
复制代码
JVM 虚拟机 ShutdownHook 注册

在 Spring 的 AbstractApplicationContext 中提供了一个 registerShutdownHook() 的方法,它可以向 JVM 注册一个钩子线程,在 JVM 进程终止的时候启动这个线程,完成对容器的销毁操作。在我们的业务代码中可以手动调用这个方法注册。代码如下:
  1. public class AbstractApplicationContext {
  2.     public void registerShutdownHook() {
  3.         if (this.shutdownHook == null) {
  4.             // No shutdown hook registered yet.
  5.             this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
  6.                 @Override
  7.                 public void run() {
  8.                     if (isStartupShutdownThreadStuck()) {
  9.                         active.set(false);
  10.                         return;
  11.                     }
  12.                     startupShutdownLock.lock();
  13.                     try {
  14.                         doClose();
  15.                     }
  16.                     finally {
  17.                         startupShutdownLock.unlock();
  18.                     }
  19.                 }
  20.             };
  21.             Runtime.getRuntime().addShutdownHook(this.shutdownHook);
  22.         }
  23.     }
  24. }
  25. public clas SpringTest {
  26.     public static void main(String[] args) {
  27.     // 1. 创建并初始化 Spring 容器
  28.     AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
  29.     // 2. 注册 JVM 关闭Hook
  30.     //    这一步是显式调用的,由开发者决定何时执行
  31.     context.registerShutdownHook();
  32.     }
  33. }
复制代码
而在 Spring Boot 项目中则提供了 SpringApplicationShutdownHook 回调类,在 Spring Boot 项目启动时,通过判断 spring.main.register-shutdown-hook 来决定是否注册回调。代码如下:
  1. public class SpringApplication {
  2.     public ConfigurableApplicationContext run(String... args) {
  3.         Startup startup = Startup.create();
  4.         if (this.properties.isRegisterShutdownHook()) {
  5.             SpringApplication.shutdownHook.enableShutdownHookAddition();
  6.         }
  7.     }
  8.     private void refreshContext(ConfigurableApplicationContext context) {
  9.         if (this.properties.isRegisterShutdownHook()) {
  10.             shutdownHook.registerApplicationContext(context);
  11.         }
  12.         refresh(context);
  13.     }
  14. }
  15. class SpringApplicationShutdownHook implements Runnable {
  16.     void registerApplicationContext(ConfigurableApplicationContext context) {
  17.         addRuntimeShutdownHookIfNecessary();
  18.         synchronized (SpringApplicationShutdownHook.class) {
  19.             assertNotInProgress();
  20.             context.addApplicationListener(this.contextCloseListener);
  21.             this.contexts.add(context);
  22.         }
  23.     }
  24.     private void addRuntimeShutdownHookIfNecessary() {
  25.         if (this.shutdownHookAdditionEnabled && this.shutdownHookAdded.compareAndSet(false, true)) {
  26.             addRuntimeShutdownHook();
  27.         }
  28.     }
  29.     void addRuntimeShutdownHook() {
  30.         Runtime.getRuntime().addShutdownHook(new Thread(this, "SpringApplicationShutdownHook"));
  31.     }
  32.    
  33.     @Override
  34.     public void run() {
  35.         Set<ConfigurableApplicationContext> contexts;
  36.         Set<ConfigurableApplicationContext> closedContexts;
  37.         List<Handler> handlers;
  38.         synchronized (SpringApplicationShutdownHook.class) {
  39.             this.inProgress = true;
  40.             contexts = new LinkedHashSet<>(this.contexts);
  41.             closedContexts = new LinkedHashSet<>(this.closedContexts);
  42.             handlers = new ArrayList<>(this.handlers.getActions());
  43.             Collections.reverse(handlers);
  44.         }
  45.         //这里调用了容器的closeAndWait方法
  46.         contexts.forEach(this::closeAndWait);
  47.         closedContexts.forEach(this::closeAndWait);
  48.         handlers.forEach(Handler::run);
  49.     }
  50. }
复制代码
Bean 销毁方法调用

注册的回调线程启动的时候会调用 AbstractApplicationContext 的 doClose() 方法,然后调用 destroyBeans() 方法,然后会调用到 DefaultSingletonBeanRegistry 的 destroySingletons() 方法。代码如下:
  1. public abstract class AbstractApplicationContext {
  2.     protected void doClose() {
  3.         // Check whether an actual close attempt is necessary...
  4.         if (this.active.get() && this.closed.compareAndSet(false, true)) {
  5.             //省略代码
  6.             // Destroy all cached singletons in the context's BeanFactory.
  7.             destroyBeans();
  8.     }
  9.    
  10.     protected void destroyBeans() {
  11.         getBeanFactory().destroySingletons();
  12.     }
  13. }
  14. public class DefaultSingletonBeanRegistry {
  15.     public void destroySingletons() {
  16.         this.singletonsCurrentlyInDestruction = true;
  17.         String[] disposableBeanNames;
  18.         synchronized (this.disposableBeans) {
  19.             disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
  20.         }
  21.         for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
  22.             destroySingleton(disposableBeanNames[i]);
  23.         }
  24.         this.containedBeanMap.clear();
  25.         this.dependentBeanMap.clear();
  26.         this.dependenciesForBeanMap.clear();
  27.         this.singletonLock.lock();
  28.         try {
  29.             clearSingletonCache();
  30.         }
  31.         finally {
  32.             this.singletonLock.unlock();
  33.         }
  34.     }
  35. }
复制代码
在 DefaultSingletonBeanRegistry 的 destroySingletons() 方法会循环调用之前注册的 DisposableBeanAdapter 的 destroy() 方法,在该方法中实现了对销毁生命周期方法的调用。这里面首先调用的是 DestructionAwareBeanPostProcessor 的 postProcessBeforeDestruction() 方法,在该方法中实现了对 @PreDestroy 注解修饰的方法的调用,然后调用 DisposableBean 的 destroy() 方法,最后再调用自定义的销毁方法。代码如下:
  1. public void destroy() {
  2.     if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
  3.         for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
  4.             //调用DestructionAwareBeanPostProcessor的postProcessBeforeDestruction()方法
  5.             processor.postProcessBeforeDestruction(this.bean, this.beanName);
  6.         }
  7.     }
  8.     if (this.invokeDisposableBean) {
  9.         try {
  10.             //调用DisposableBean的destroy()方法
  11.             ((DisposableBean) this.bean).destroy();
  12.         }
  13.         catch (Throwable ex) {
  14.             
  15.         }
  16.     } else if (this.destroyMethods != null) {
  17.         for (Method destroyMethod : this.destroyMethods) {
  18.             //调用自定义初始化方法
  19.             invokeCustomDestroyMethod(destroyMethod);
  20.         }
  21.     }
  22.    
  23. }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册