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) {// TODOSpringApplication.exit(this.context, () -> ExitCode.INIT_ERROR) ;}// 类没有发现错误if (e instanceof NoClassDefFoundError err) {// TODOSpringApplication.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 ;}@Overridepublic int getOrder() {return 1 ;}
}
注:order值越小优先级越高。
2.3 退出事件监听
当exitCode返回值不为0时,Spring Boot会发布ExitCodeEvent事件,我们可以监听该事件进行相应的处理。
@Component
public class ExitCodeEventListener implements ApplicationListener<ExitCodeEvent> {@Overridepublic void onApplicationEvent(ExitCodeEvent event) {System.out.printf("程序准备退出, 退出码: %d%n", event.getExitCode()) ;}
}
输出结果