舒娅友 发表于 2025-5-30 13:34:12

一个 Bean 就这样走完了它的一生之 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);
      }

      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);
      }
    }
   
}
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

忆雏闲 发表于 7 天前

感谢分享,学习下。
页: [1]
查看完整版本: 一个 Bean 就这样走完了它的一生之 Bean 的消亡