如何在无Spring环境下优雅关闭嵌入式Tomcat?
嵌入式Tomcat优雅关闭实现方案
问题根源
你当前手动销毁连接器、上下文的操作打乱了Tomcat自身的生命周期管理流程,导致TS_TomcatListener.contextDestroyed无法被正常触发。Tomcat的生命周期方法(如stop())会自动处理连接器停止、上下文销毁、监听器回调等步骤,手动干预会跳过这些内置逻辑。
正确实现步骤
- 注册JVM关闭钩子:捕获进程终止信号(如
Ctrl+C、kill命令),确保触发优雅关闭流程 - 调用Tomcat原生生命周期方法:不要手动销毁连接器或上下文,通过
stop()和destroy()完成完整关闭 - 确保监听器正确注册:将自定义
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
相关产品推荐
相关产品推荐

