环境:SpringBoot3.2.5
1. 简介
ExitCodeGenerator是 Spring Boot 框架中的一个接口,它允许应用程序退出时生成自定义的退出代码。你可以根据不同的退出码,执行相应的动作,如:资源清理,日志记录等。
我们可以通过实现ExitCodeGenerator接口并注册为 Bean。然后,在需要的时候,可以调用SpringApplication#exit方法并传递相应的 ApplicationContext 和ExitCodeGenerator实例来触发应用程序的退出,并返回由 ExitCodeGenerator生成的退出代码。
ExitCodeGenerator的应用场景包括但不限于自定义错误处理、应用程序状态报告、系统监控与告警以及优雅地关闭应用程序。通过实现和使用 ExitCodeGenerator,开发者可以更加灵活地控制和管理 Spring Boot 应用程序的退出行为,从而提高系统的可靠性和可维护性。
2. 实战案例
关于退出码exitCode说明:一般0表示成功,其它数字表示因异常而退出。
2.1 根据不同的异常退出程序
定义异常代码
public interface ExitCode {
/**初始化错误*/
int INIT_ERROR = 1 ;
/**类没有发现错误*/
int NOT_FOUND_CLASS_ERROR = 2 ;
/**内存溢出错误*/
int OOM_ERROR = 3 ;
}
上面定义了3中错误类型
定义异常退出
@Component
public class AppExit {
private final ApplicationContext context ;
public AppExit(ApplicationContext context) {
this.context = context ;
}
public void exit(Throwable e) {
// 初始化错误
if (e instanceof ExceptionInInitializerError err) {
// TODO
SpringApplication.exit(this.context, () -> ExitCode.INIT_ERROR) ;
}
// 类没有发现错误
if (e instanceof NoClassDefFoundError err) {
// TODO
SpringApplication.exit(this.context, () -> ExitCode.NOT_FOUND_CLASS_ERROR) ;
}
// OOM错误
if (e instanceof OutOfMemoryError err) {
System.err.println("发生OOM,内存溢出了,程序准备退出") ;
SpringApplication.exit(this.context, () -> ExitCode.OOM_ERROR) ;
}
}
}
该类定义了不同错误时执行不同的退出逻辑。
定义全局错误拦截
@RestControllerAdvice
public static class ErrorControllerAdvice {
private final AppExit appExit ;
public ErrorControllerAdvice(AppExit appExit) {
this.appExit = appExit ;
}
// 退出程序,所以无需返回值
@ExceptionHandler({ExceptionInInitializerError.class, NoClassDefFoundError.class, OutOfMemoryError.class})
public void error(Throwable e) {
this.appExit.exit(e.getCause()) ;
}
}
该错误拦截器会对上面3个Error进行处理。
测试代码
这里模拟OOM错误
List<byte[]> list = new ArrayList<>() ;
@GetMapping("")
public Object exit() {
while(true) {
try {
TimeUnit.MILLISECONDS.sleep(100) ;
}
list.add(new byte[200 * 1024 * 1024]) ;
}
}
启动SpringBoot程序时设置堆内存
-Xmx100m -Xmx100m
测试结果
我们的程序也退出了。
2.2 ExitCodeGenerator定义为Bean
我们可以将多个ExitCodeGenerator定义为bean。当执行退出时,会遍历所有的对象,直到exitCode不为0时退出。如下示例:
@Component
public class ErrorInitializerExitCode implements ExitCodeGenerator {
public int getExitCode() {
System.err.printf("初始化错误退出...") ;
return ExitCode.INIT_ERROR ;
}
}
@Component
public class ErrorClassNotFoundExitCode implements ExitCodeGenerator {
public int getExitCode() {
System.err.printf("类没有发现错误退出...") ;
return ExitCode.NOT_FOUND_CLASS_ERROR ;
}
}
@Component
public class ErrorOOMExitCode implements ExitCodeGenerator {
public int getExitCode() {
System.err.printf("OOM错误退出...") ;
return ExitCode.OOM_ERROR ;
}
}
上面示例定义了3个ExitCodeGenerator Bean实例,当调用退出方法时,会遍历这3个Bean对象,通过正常的代码调用退出方法
@GetMapping("/e")
public Object quit() {
try {
// 调用接口后,能返回正常的值
return "success quit" ;
} finally {
new Thread(() -> {
try {
TimeUnit.MILLISECONDS.sleep(500) ;
} catch (InterruptedException e) {}
SpringApplication.exit(this.context) ;
}).start() ;
}
}
输出结果
这里的输出就与具体什么异常没有关系了,只要遇到第一个返回不为0的数字,则直接退出了。
如果你希望控制这些ExitCode的顺序,那么你可以实现Ordered接口或者使用@Order注解。如下示例:
@Component
public static class ErrorInitializerExitCode implements ExitCodeGenerator, Ordered {
public int getExitCode() {
System.err.printf("初始化错误退出...%n") ;
return ExitCode.INIT_ERROR ;
}
@Override
public int getOrder() {
return 1 ;
}
}
注:order值越小优先级越高。
2.3 退出事件监听
当exitCode返回值不为0时,Spring Boot会发布ExitCodeEvent事件,我们可以监听该事件进行相应的处理。
@Component
public class ExitCodeEventListener implements ApplicationListener<ExitCodeEvent> {
@Override
public void onApplicationEvent(ExitCodeEvent event) {
System.out.printf("程序准备退出, 退出码: %d%n", event.getExitCode()) ;
}
}
输出结果