Mastering Spring Boot ExitCodeGenerator: Custom Exit Codes & Graceful Shutdown
Learn how to implement Spring Boot's ExitCodeGenerator to define custom exit codes, handle various exceptions, register beans, intercept errors, and listen to exit events, enabling graceful application shutdown and improved reliability with practical code examples and configuration tips.
Environment: SpringBoot 3.2.5
1. Introduction
ExitCodeGenerator is an interface in the Spring Boot framework that allows an application to generate custom exit codes when it terminates. By implementing this interface and registering it as a bean, developers can trigger SpringApplication.exit with a specific ApplicationContext and ExitCodeGenerator instance, causing the application to exit with the generated code.
Typical use cases include custom error handling, application state reporting, system monitoring and alerts, and graceful shutdown, which improve reliability and maintainability.
2. Practical Example
2.1 Exit based on different exceptions
Define error codes:
<code>public interface ExitCode {
/** Initialization error */
int INIT_ERROR = 1;
/** Class not found error */
int NOT_FOUND_CLASS_ERROR = 2;
/** Out of memory error */
int OOM_ERROR = 3;
}</code>Define a component that performs exit logic based on the exception type:
<code>@Component
public class AppExit {
private final ApplicationContext context;
public AppExit(ApplicationContext context) { this.context = context; }
public void exit(Throwable e) {
// Initialization error
if (e instanceof ExceptionInInitializerError) {
SpringApplication.exit(this.context, () -> ExitCode.INIT_ERROR);
}
// Class not found error
if (e instanceof NoClassDefFoundError) {
SpringApplication.exit(this.context, () -> ExitCode.NOT_FOUND_CLASS_ERROR);
}
// OOM error
if (e instanceof OutOfMemoryError) {
System.err.println("OOM occurred, program will exit");
SpringApplication.exit(this.context, () -> ExitCode.OOM_ERROR);
}
}
}</code>Global error interceptor handling the three errors:
<code>@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());
}
}</code>Test code that simulates an OOM error:
<code>List<byte[]> list = new ArrayList<>();
@GetMapping("")
public Object exit() {
while (true) {
try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException ignored) {}
list.add(new byte[200 * 1024 * 1024]); // allocate 200MB
}
}</code>Run the Spring Boot program with limited heap memory (e.g., -Xmx100m ). The application exits as shown in the screenshot:
2.2 Registering ExitCodeGenerator as beans
Multiple ExitCodeGenerator beans can be defined; Spring Boot will iterate them and exit when a non‑zero code is returned:
<code>@Component
public class ErrorInitializerExitCode implements ExitCodeGenerator {
public int getExitCode() { System.err.printf("Init error exit...\n"); return ExitCode.INIT_ERROR; }
}
@Component
public class ErrorClassNotFoundExitCode implements ExitCodeGenerator {
public int getExitCode() { System.err.printf("Class not found exit...\n"); return ExitCode.NOT_FOUND_CLASS_ERROR; }
}
@Component
public class ErrorOOMExitCode implements ExitCodeGenerator {
public int getExitCode() { System.err.printf("OOM exit...\n"); return ExitCode.OOM_ERROR; }
}</code>Endpoint that triggers exit after returning a normal response:
<code>@GetMapping("/e")
public Object quit() {
try { return "success quit"; }
finally {
new Thread(() -> {
try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException ignored) {}
SpringApplication.exit(this.context);
}).start();
}
}</code>Result screenshot:
If you need to control the order of execution, implement Ordered or use @Order :
<code>@Component
public static class ErrorInitializerExitCode implements ExitCodeGenerator, Ordered {
public int getExitCode() { System.err.printf("Init error exit...%n"); return ExitCode.INIT_ERROR; }
@Override
public int getOrder() { return 1; }
}</code>Note: lower order value means higher priority.
2.3 Listening to exit events
When a non‑zero exit code is returned, Spring Boot publishes an ExitCodeEvent . You can listen to it for additional processing:
<code>@Component
public class ExitCodeEventListener implements ApplicationListener<ExitCodeEvent> {
@Override
public void onApplicationEvent(ExitCodeEvent event) {
System.out.printf("Application is exiting, exit code: %d%n", event.getExitCode());
}
}</code>Output screenshot:
The article demonstrates how to use ExitCodeGenerator , global error handling, bean registration, ordering, and event listening to achieve graceful shutdown and better control over application termination in Spring Boot.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.