Spring优雅关闭:ShutDownHook实现资源释放与异常处理

Spring优雅关闭:ShutDownHook实现资源释放与异常处理

程序在异常结束的情况下,有可能无法及时释放某些资源。通过预埋钩子这种方式,能够在异常退出时进行捕获。

获取运行时对象。然后通过该对象调用 addShutdownHook 方法。

我们模拟一个异常情况。程序报错之后,钩子函数会开始执行。通过使用 Runtime 来添加钩子函数,就能够实现优雅停机。

  public static void main(String[] args) throws Exception {
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("正在关闭一些连接资源...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ignore) {
                }
            }
        }));
        
        // 模拟执行业务耗时
        int i = 0;
        while (true) {
            System.out.println("i am working...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ignore) {
            }
            // 模拟一个异常
            if (i++ > 5) {
                throw new Exception("error.");
            }
        }
    }

这个钩子对 kill(kill -15) 是有效的,然而对 kill -9 却是无效的。所以在生产中不得已要使用 kill 时,通常会先使用 kill ,一般不会使用 kill -9 。

适用的场景:

Spring如何添加钩子函数,实现优雅关闭

ApplicationContext 注册了关闭钩子。

可以配置的应用上下文。 applicationContext = SpringApplication.run(SpringBootNonWebApplication.class, args);
applicationContext.registerShutdownHook();

ClassPathXmlApplicationContext 是一个用于加载 XML 配置文件的 Spring 应用上下文类。它可以从类路径中读取配置文件,并创建和管理 Spring 应用中的各种 Bean。通过使用 ClassPathXmlApplicationContext,开发人员可以方便地配置和启动 Spring 应用,而无需手动创建和管理 Bean 的实例。它提供了一种简单而有效的方式来管理 Spring 应用的依赖关系和配置信息。 applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    // 注册钩子
applicationContext.registerShutdownHook();

// 通过源码可以看到

@Override
public void registerShutdownHook() {
    if (this.shutdownHook == null) {
        尚未注册关闭钩子。
        this.shutdownHook = new Thread() {
            @Override
            public void run() {
                synchronized (startupShutdownMonitor) {
                    doClose();
                }
            }
        };
        // 也是通过这种方式来添加
        Runtime.getRuntime().addShutdownHook(this.shutdownHook);
    }
}

重点是这个doClose()方法:

protected void doClose() {
    检查是否有实际的关闭尝试是必要的……
    if (this.active.get() && this.closed.compareAndSet(false, true)) {
        if (logger.isInfoEnabled()) {
            logger.info("Closing " + this);
        }
 
        LiveBeansView.unregisterApplicationContext(this);
 
        try {
            // Publish shutdown event.
            publishEvent(new ContextClosedEvent(this));
        }
        catch (Throwable ex) {
            logger.warn(ApplicationListener 处理 ContextClosedEvent 时抛出了异常。, ex);
        }
 
        停止所有的生命周期 Bean,以避免在单个销毁过程中出现延迟。
        if (this.lifecycleProcessor != null) {
            try {
                this.lifecycleProcessor.onClose();
            }
            catch (Throwable ex) {
                logger.warn(在上下文关闭时,从生命周期处理器抛出了异常。, ex);
            }
        }
 
        在上下文的 BeanFactory 中销毁所有缓存的单例。
        destroyBeans();
 
        关闭这个上下文自身的状态。
        closeBeanFactory();
 
        如果子类愿意的话,让它们进行一些最后的清理工作。
        onClose();
 
        // Switch to inactive.
        this.active.set(false);
    }
}

doClose()方法会去执行 bean 的 destroy(),同时也会执行 SmartLifeCycle 的 stop()方法。我们能够通过对这些方法进行重写,借助对象生命周期的管理,从而实现平滑的 shutdown。

结合这一篇:

Spring 定义 Bean 的 init Method 有三种方式;Spring 定义 Bean 的 destroy Method 有三种方式。

当创建一个 bean 的时候,能够指定 initMethod 以及 destroyMethod 。

@Configuration
public class XxDataSourceConfiguration {
    @Bean(initMethod = "myInitMethod", destroyMethod = "myDestroyMethod")
    public XxDataSource getBean(){
        return new XxDataSource();
    }
}
class XxDataSource {
    public void myInitMethod() {
        System.out.println("我是一个init方法...");
    }
    public void myDestroyMethod() {
        System.out.println("我是一个destroy方法...");
    }
}

【Spring】Lifecycle的使用与源码分析

Spring 容器的生命周期由 LifeCycle 接口定义,凡是被 Spring 管理的对象都能够实现该接口。Spring 容器自身在启动和停止的时候,会调用 LifeCycle 接口中所定义的方法,即【start()】和【stop()】。

// 1)不注册,只创建容器,结果如下:
只执行了 LifeCycle.start()方法,没有其他额外操作。
    
// 2)注册钩子
start
执行了 LifeCycle.stop()方法,从而停止了 Runnable 。

我们当前讨论的是 Spring 的非 Web 程序。如果是 Web 程序,就不需要我们去注册钩子函数,因为 Spring 的 Web 程序自身已经有了相关的代码,能够实现优雅关闭。

SpringBoot 项目在异常情况下会首先执行 destroyMethod 方法。

你可能想看:
« 上一篇
下一篇 »

相关推荐

java中什么是冒泡,冒泡算法

2026年09月06日

26阅读

java中冒号,java双冒号

2026年09月06日

23阅读

java中的->,java中的是什么意思

2026年09月06日

24阅读

java中的super用法,Java中super的说明

2026年09月06日

27阅读

java中反射的应用,java中反射的应用场景

2026年09月06日

23阅读

java中开关的定义,Java关键字有哪些

2026年09月06日

24阅读