程序退出码

环境:SpringBoot3.2.5


1. 简介

ExitCodeGenerator是 Spring Boot 框架中的一个接口,它允许应用程序退出时生成自定义的退出代码。你可以根据不同的退出码,执行相应的动作,如:资源清理,日志记录等。

我们可以通过实现ExitCodeGenerator接口并注册为 Bean。然后,在需要的时候,可以调用SpringApplication#exit方法并传递相应的 ApplicationContextExitCodeGenerator实例来触发应用程序的退出,并返回由 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()) ;
  }
}

输出结果

图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值