在线上环境中,应用可能因为一些异常而终止,我们如果需要及时找到原因,根据 exit code
来定位,是个很好的途径。 spring boot
为开发者提供了相关的接口,方便开发者通过异常类型来定义自己的 exit code
: ExitCodeGenerator
和 ExitCodeExceptionMapper
.
ExitCodeGenerator
:
用于主动退出应用,在 SpringApplication.exit(org.springframework.context.ApplicationContext,ExitCodeGenerator..)
中用到,该方法会寻找所有的 ExitCodeGenerator
的 Bean
, 也会用到方法中的入参对象。
ExitCodeGenerator
声明如下:
@FunctionalInterface public interface ExitCodeGenerator { /** * Returns the exit code that should be returned from the application. * @return the exit code. */ int getExitCode(); }
System.exit(SpringApplication.exit(SpringApplication.run(DemoApplication.class, args)));
@SpringBootApplication public class DemoApplication{ @Bean public ExitCoder getTestExitCoder(){ return new ExitCoder(); } @Bean public ExitCoder1 getTestExitCoder1(){ return new ExitCoder1(); } public static void main(String[] args) { System.exit(SpringApplication.exit(SpringApplication.run(DemoApplication.class, args))); } }
import org.springframework.boot.ExitCodeGenerator; public class ExitCoder implements ExitCodeGenerator { @Override public int getExitCode() { System.out.println("get exit code from class: ExitCoder"); return 222; } }
import org.springframework.boot.ExitCodeGenerator; public class ExitCoder1 implements ExitCodeGenerator { @Override public int getExitCode() { System.out.println("get exit code from class: ExitCoder1"); return 221; } }
输出为
2019-03-21 21:52:55.802 INFO 44627 --- [ main] com.example.exitcode.DemoApplication : Starting DemoApplication on lei.local with PID 44627 (/Users/lei/own/projects/java_learning/spring_boot_learning/blog/5exitcode/exitcode/out/production/classes started by lei in /Users/lei/own/projects/java_learning/spring_boot_learning/blog/5exitcode/exitcode) 2019-03-21 21:52:55.806 INFO 44627 --- [ main] com.example.exitcode.DemoApplication : No active profile set, falling back to default profiles: default 2019-03-21 21:52:56.339 INFO 44627 --- [ main] com.example.exitcode.DemoApplication : Started DemoApplication in 15.901 seconds (JVM running for 21.676) get exit code from class: ExitCoder get exit code from class: ExitCoder1 Disconnected from the target VM, address: '127.0.0.1:50873', transport: 'socket' Process finished with exit code 222
从上面可以看到,以 exit code
最大的为最终值。
ExitCodeExceptionMapper
: 用于应用发生不可调整的异常,导致应用退出的情况。声明如下:
@FunctionalInterface public interface ExitCodeExceptionMapper { /** * Returns the exit code that should be returned from the application. * @param exception the exception causing the application to exit * @return the exit code or {@code 0}. */ int getExitCode(Throwable exception); }
通过 Bean
来注册,当应用发生异常时,会调用每个 ExitCodeExceptionMapper
的实现类。这里,我们可以根据异常类型来设置自己的 exit code
:
@SpringBootApplication public class DemoApplication{ @Bean public DemoExitCodeExceptionMapper getExitCodeMapper(){ return new DemoExitCodeExceptionMapper(); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @Bean CommandLineRunner showUsers() { return args -> {throw new Exception("xxxxx");}; } }
public class DemoExitCodeExceptionMapper implements ExitCodeExceptionMapper{ /** * Returns the exit code that should be returned from the application. * @param exception the exception causing the application to exit * @return the exit code or {@code 0}. */ @Override public int getExitCode(Throwable exception){ System.out.println("exit cod xxxx" + exception.getMessage()); if(exception.getCause().getMessage().equals("sdf")){ return 254; } return 243; } }
运行输出为:
2019-03-21 22:13:34.261 INFO 45049 --- [ main] com.example.exitcode.DemoApplication : Started DemoApplication in 15.816 seconds (JVM running for 21.521) exit cod xxxxFailed to execute CommandLineRunner 2019-03-21 22:13:38.797 INFO 45049 --- [ main] ConditionEvaluationReportLoggingListener : Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. 2019-03-21 22:13:38.845 ERROR 45049 --- [ main] o.s.boot.SpringApplication : Application run failed java.lang.IllegalStateException: Failed to execute CommandLineRunner at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:816) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE] at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:797) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:324) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE] at com.example.exitcode.DemoApplication.main(DemoApplication.java:31) [classes/:na] Caused by: java.lang.Exception: xxxxx at com.example.exitcode.DemoApplication.lambda$showUsers$0(DemoApplication.java:37) [classes/:na] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:813) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE] ... 5 common frames omitted Disconnected from the target VM, address: '127.0.0.1:51237', transport: 'socket' Process finished with exit code 243
public int getExitCode() { int exitCode = 0; for (ExitCodeGenerator generator : this.generators) { try { int value = generator.getExitCode(); if (value > 0 && value > exitCode || value < 0 && value < exitCode) { exitCode = value; } } catch (Exception ex) { exitCode = (exitCode != 0) ? exitCode : 1; ex.printStackTrace(); } } return exitCode; }
n
和 n-1
正负不同,取 n
, 相同,取绝对值大的。 SpringApplication.exit
方法使用 ExitCodeGenerator
,可以通过 Bean
注册,也可通过传值。 ExitCodeExceptionMapper
, 只能通过 Bean
注册使用。