You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在无Spring环境下优雅关闭嵌入式Tomcat?

嵌入式Tomcat优雅关闭实现方案

问题根源

你当前手动销毁连接器、上下文的操作打乱了Tomcat自身的生命周期管理流程,导致TS_TomcatListener.contextDestroyed无法被正常触发。Tomcat的生命周期方法(如stop())会自动处理连接器停止、上下文销毁、监听器回调等步骤,手动干预会跳过这些内置逻辑。

正确实现步骤

  1. 注册JVM关闭钩子:捕获进程终止信号(如Ctrl+C、kill命令),确保触发优雅关闭流程
  2. 调用Tomcat原生生命周期方法:不要手动销毁连接器或上下文,通过stop()和destroy()完成完整关闭
  3. 确保监听器正确注册:将自定义TS_TomcatListener绑定到Tomcat上下文

代码示例

Tomcat 9(javax版本)

import org.apache.catalina.startup.Tomcat;
import javax.servlet.ServletContextListener;

public class EmbeddedTomcat9 {
    public static void main(String[] args) throws Exception {
        Tomcat tomcat = new Tomcat();
        // 配置端口、上下文路径等基础参数
        tomcat.setPort(8080);
        tomcat.addWebapp("/", System.getProperty("user.dir") + "/webapp");

        // 注册自定义监听器
        ServletContextListener listener = new TS_TomcatListener();
        tomcat.getHost().findChild("/").addApplicationListener(listener);

        // 启动Tomcat
        tomcat.start();

        // 注册关闭钩子,处理优雅关闭
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                // 设置连接器优雅关闭超时(5秒,等待剩余请求处理完成)
                tomcat.getConnector().setPortWithTimeout(-1, 5000);
                
                // 触发Tomcat完整停止流程,自动调用监听器contextDestroyed
                tomcat.stop();
                tomcat.destroy();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }));

        // 阻塞主线程,保持Tomcat运行
        tomcat.getServer().await();
    }
}

Tomcat 10(Jakarta版本)

import org.apache.catalina.startup.Tomcat;
import jakarta.servlet.ServletContextListener;

public class EmbeddedTomcat10 {
    public static void main(String[] args) throws Exception {
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);
        tomcat.addWebapp("/", System.getProperty("user.dir") + "/webapp");

        // 注册自定义监听器
        ServletContextListener listener = new TS_TomcatListener();
        tomcat.getHost().findChild("/").addApplicationListener(listener);

        tomcat.start();

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                tomcat.getConnector().setPortWithTimeout(-1, 5000);
                tomcat.stop();
                tomcat.destroy();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }));

        tomcat.getServer().await();
    }
}

关键注意事项

  • 不要手动调用destroyConnector()、destroyContext()等方法,这些是Tomcat内部生命周期的执行步骤,手动调用会跳过监听器回调逻辑
  • tomcat.stop()会按顺序执行:停止连接器→处理剩余请求→销毁上下文→触发contextDestroyed→停止服务器
  • 如果需要主动触发关闭(而非依赖JVM信号),直接调用tomcat.stop()和tomcat.destroy()即可
  • 确保TS_TomcatListener正确实现对应版本的ServletContextListener接口,重写contextDestroyed方法

内容的提问来源于stack exchange,提问作者Tugalsan Karabacak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 14:35:20