How to Gracefully Shut Down a Spring Boot Application
This article explains why force‑killing a Spring Boot process is unsafe and walks through four practical ways—using Actuator, ApplicationContext.close(), PID‑based kill commands, and SpringApplication.exit()—to achieve a graceful shutdown while preserving in‑flight tasks and releasing resources.
Graceful shutdown means that after a stop command the application stops accepting new requests, lets ongoing requests, scheduled jobs, and message‑consumer tasks finish, and then notifies dependent services before terminating, avoiding data loss or resource leaks such as unreleased Redis locks.
Using kill -9 <pid> terminates the JVM abruptly, which can corrupt in‑flight data and leave global resources unreleased.
Spring Boot’s official documentation suggests implementing specific interfaces to hook into startup and shutdown events. The key interfaces are: CommandLineRunner (or ApplicationRunner) – called after the application starts but before it begins handling traffic. DisposableBean or the @PreDestroy annotation – called just before the bean container is destroyed.
An example listener that prints messages on start and shutdown:
@Component
public class AppListener implements CommandLineRunner, DisposableBean {
@Override
public void run(String... args) throws Exception {
System.out.println("Application started, pre‑loading data");
}
@Override
public void destroy() throws Exception {
System.out.println("Application shutting down, cleaning up data");
}
}Each SpringApplication registers a JVM shutdown hook that triggers the ApplicationContext to close, ensuring the above callbacks are invoked.
Method 1: Actuator shutdown endpoint
Add the dependency spring-boot-starter-actuator and enable the endpoint:
management.endpoint.shutdown.enabled=true
management.endpoints.web.exposure.include=shutdownThen send a POST request to http://127.0.0.1:8080/actuator/shutdown to stop the service.
Method 2: Directly close the ApplicationContext
Obtain the ConfigurableApplicationContext from SpringApplication.run and call close() when needed. Example with a 10‑second delay:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
context.close();
}
}A REST controller can expose a /shutdown endpoint that casts the injected ApplicationContext to ConfigurableApplicationContext and invokes close():
@RestController
public class ShutdownController implements ApplicationContextAware {
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
@GetMapping("/shutdown")
public void shutdownContext() {
((ConfigurableApplicationContext) context).close();
}
}Method 3: PID file and kill command
Configure Spring Boot to write its process ID to a file using ApplicationPidFileWriter:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.addListeners(new ApplicationPidFileWriter("/home/app/project1/app.pid"));
app.run(args);
}
}Then safely stop the service with:
cat /home/app/project1/app.pid | xargs killMethod 4: SpringApplication.exit()
Call SpringApplication.exit(context, () -> 0) to obtain an exit code and then terminate the JVM:
@SpringBootApplication
public class Application {
public static void main(String[] args) throws InterruptedException {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
TimeUnit.SECONDS.sleep(5);
int exitCode = SpringApplication.exit(context, () -> 0);
System.exit(exitCode);
}
}Additional listeners
Implement ApplicationListener<ContextClosedEvent> to run custom logic (e.g., stop scheduled jobs) before the bean container is destroyed:
@Component
public class JobTaskListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextClosedEvent) {
System.out.println("Shutting down related scheduled tasks");
}
}
}Or annotate a method with @PreDestroy for the same effect:
@Component
public class AppDestroyConfig {
@PreDestroy
public void preDestroy() {
System.out.println("Application is shutting down…");
}
}These approaches collectively provide safe ways to stop a Spring Boot service without losing in‑flight work or leaving resources dangling.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Pan Zhi's Tech Notes
Sharing frontline internet R&D technology, dedicated to premium original content.
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.
