springboot 优雅关闭_Spring Boot 优雅的关闭

本文介绍了四种优雅关闭Spring Boot应用的方法:利用Actuator、调用`ConfigurableApplicationContext`的`close()`方法、根据PID杀死进程和使用`SpringApplication.exit()`。在关闭过程中,会调用`@PreDestroy`标记的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文将介绍几种方式来优雅的关闭 Spring Boot,分别如下:

准备项目

假如我们创建了一个 Spring Boot 项目,该项目的结构如下图:

其中,TerminateBean 类的代码如下:package com.huangx.springboot.springboot_shutdown_demo1.bean;

import javax.annotation.PreDestroy;

public class TerminateBean {

@PreDestroy

public void preDestroy() {

System.out.println("TerminalBean is destroyed");

}

}

上面类中,使用 @PreDestroy 标记 preDestroy() 方法,表示当 Spring Boot 关闭前将会调用该方法。

ShutDownConfig 类的代码如下:package com.huangx.springboot.springboot_shutdown_demo1.config;

import com.huangx.springboot.springboot_shutdown_demo1.bean.TerminateBean;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class ShutDownConfig {

@Bean

public TerminateBean getTerminateService() {

return new TerminateBean();

}

}

上面代码使用 @Configuration 和 @Bean 注解将 TerminateBean 配置到 Spring Boot,Spring Boot 关闭时才能调用它的 preDestroy() 方

法。

在下面介绍的几种关闭 Spring Boot 的方式中,仅仅只有 SpringbootShutdownDemo1Application 类代码实现不同。上面两个类的代码均是一致的,因此就不会赘述。

利用 Spring Boot 的 actuator

Spring Boot 自带监控功能 Actuator,可以帮助实现对程序内部运行情况监控,比如监控状况、Bean加载情况、环境变量、日志信息、线程信息等。我们将利用 Actuator 实现关闭 Spring Boot,如下:

(1)添加 actuator 的 maven 依赖,如下:

org.springframework.boot

spring-boot-starter-actuator

(2)默认情况下,actuator 的 shutdown 是禁用的,我们需要打开它。修改 application.properties 或 application.yml 文件,如下:# 配置远程关闭 SpringBoot

management.endpoint.shutdown.enabled=true

management.endpoints.web.exposure.include=shutdown

(3)SpringbootShutdownDemo1Application 类代码如下:@SpringBootApplication

@RestController

public class SpringbootShutdownDemo1Application {

public static void main(String[] args) {

SpringApplication.run(SpringbootShutdownDemo1Application.class, args);

}

@RequestMapping("/")

public String index() {

return "Spring Boot Shutdown Demo1";

}

}

(4)启动 Spring Boot 项目,使用浏览器访问 http://localhost:8080/actuator  将会输出如下信息:{

"_links":{

"self":{

"href":"http://localhost:8080/actuator",

"templated":false

},

"shutdown":{

"href":"http://localhost:8080/actuator/shutdown",

"templated":false

}

}

}

我们可以通过调用 http://localhost:8080/actuator/shutdown 去关闭 Spring Boot 应用(注意:该接口需要 POST 提交),如果调用成功,接口返回如下信息:{

"message": "Shutting down, bye..."

}

同时,Spring Boot 将会调用 TerminateBean 对象的 preDestroy() 方法,如下:

调用 ConfigurableApplicationContext 的 close 方法

我们直接调用 ConfigurableApplicationContext 对象的 close() 方法。其中,SpringApplication.run() 方法将返回 ConfigurableApplicationContext 对象。我们可以提供一个 Restful 接口 /shutdown 实现通过接口方式关闭 Spring Boot 程序。代码如下:@SpringBootApplication

@RestController

public class SpringbootShutdownDemo2Application {

private static ConfigurableApplicationContext content;

public static void main(String[] args) {

content = SpringApplication.run(SpringbootShutdownDemo2Application.class, args);

}

@RequestMapping("/")

public String index() {

return "Spring Boot Shutdown Demo2";

}

@RequestMapping("/shutdown")

public void shutdown() {

try {

if(null == content) {

throw new NullPointerException("not ConfigurableApplicationContext object");

}

content.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

启动程序后,调用 http://localhost:8080/shutdown  该接口支持 GET 和 POST。执行效果如下:

注意:TerminateBean 对象的 preDestroy() 方法依然被调用了。

根据 pid 杀死进程

在 Spring Boot 启动的时候将进程号写入一个 pid 文件(如:my.pid),生成的路径你可以任意指定。然后通过 DOS命令或者Shell命令根据 PID Kill 进程即可。这个时候 TerminateBean 对象的 preDestroy() 方法也会调用的。这种方法大家使用的比较普遍。写一个 start.sh/start.bat 脚本用于启动 Spring Boot 程序,然后写一个 shutdown.sh/shutdown.bat 停止脚本将服务停止。实现如下:package com.huangx.springboot.springboot_shutdown_demo3;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.boot.context.ApplicationPidFileWriter;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import java.io.File;

import java.io.FileInputStream;

@SpringBootApplication

@RestController

public class SpringbootShutdownDemo3Application {

public static void main(String[] args) {

SpringApplication application =

new SpringApplication(SpringbootShutdownDemo3Application.class);

String tmpDir = System.getProperty("java.io.tmpdir");

application.addListeners(new ApplicationPidFileWriter(tmpDir + "/shutdown.pid"));

application.run();

}

@RequestMapping("/")

public String index() {

return "Spring Boot Shutdown Demo3";

}

@RequestMapping("/shutdown")

public void shutdown() {

try {

String pidFilePath = System.getProperty("java.io.tmpdir") + "/shutdown.pid";

File file = new File(pidFilePath);

if(!file.exists() || !file.isFile()) {

throw new IllegalArgumentException("pid 文件不存在或不是一个文件");

}

// 获取 PID

FileInputStream inputStream = new FileInputStream(file);

byte[] bytes = new byte[inputStream.available()];

inputStream.read(bytes);

inputStream.close();

String pid = new String(bytes);

System.out.println("PID=" + pid);

// 根据 pid 结束进程

boolean flag = cmd("taskkill /pid " + pid + " /F");

if(flag) {

file.delete(); // 删除 PID 文件

System.out.println("kill process success");

} else {

System.err.println("kill process fail");

}

} catch (Exception e) {

e.printStackTrace();

}

}

private static boolean cmd(String command){

boolean flag = false;

try{

Runtime.getRuntime().exec("cmd.exe /C "+command);

flag = true;

}catch(Exception e){

e.printStackTrace();

}

return flag;

}

}

上面代码,将 pid 保存到临时目录的 shutdown.pid 文件,然后通过调用 /shutdown 接口去读取 pid,使用 Windows 的“taskkill /pid /F”命令强制结束进程。结果如下图:

根据结果得知,该种方法并没有触发TerminateBean 对象的 preDestroy() 方法。

SpringApplication.exit() 方法

直接通过调用 SpringApplication.exit()方法也可以退出程序,同时将生成一个退出码,这个退出码可以传递给所有的 context。这就是一个JVM 的钩子,通过调用这个方法的话会把 TerminateBean 对象的 preDestroy() 方法执行并停止,并且传递给具体的退出码给所有 Context。通过调用 System.exit(exitCode) 可以将这个错误码也传给 JVM。程序执行完后最后会输出:Process finished with exit code 0,给 JVM 一个信号。代码如下:package com.huangx.springboot.springboot_shutdown_demo4;

import org.springframework.boot.ExitCodeGenerator;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.ConfigurableApplicationContext;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication

@RestController

public class SpringbootShutdownDemo4Application {

private static ConfigurableApplicationContext context;

public static void main(String[] args) {

context = SpringApplication.run(SpringbootShutdownDemo4Application.class, args);

}

@RequestMapping("/")

public String index() {

return "Spring Boot Shutdown Demo4";

}

@RequestMapping("/shutdown")

public void shutdown() {

try {

int exitCode = SpringApplication.exit(context, new ExitCodeGenerator(){

@Override

public int getExitCode() {

return 0;

}

});

System.exit(exitCode);

} catch (Exception e) {

e.printStackTrace();

}

}

}

启动 Spring Boot 程序,调用 /shutdown 接口,输出结果如下图:

注意:TerminateBean 对象的 preDestroy() 方法依然被调用了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值