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

2 阅读5分钟

生命周期流程

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

案例解析

数据库连接池在 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 创建的过程中,在 AbstractAutowireCapableBeanFactorydoCreateBean() 方法中会判断一个 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 销毁方法调用

注册的回调线程启动的时候会调用 AbstractApplicationContextdoClose() 方法,然后调用 destroyBeans() 方法,然后会调用到 DefaultSingletonBeanRegistrydestroySingletons() 方法。代码如下:

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();
        }
    }
}

DefaultSingletonBeanRegistrydestroySingletons() 方法会循环调用之前注册的 DisposableBeanAdapterdestroy() 方法,在该方法中实现了对销毁生命周期方法的调用。这里面首先调用的是 DestructionAwareBeanPostProcessorpostProcessBeforeDestruction() 方法,在该方法中实现了对 @PreDestroy 注解修饰的方法的调用,然后调用 DisposableBeandestroy() 方法,最后再调用自定义的销毁方法。代码如下:

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);
        }
    }
    
}