1. 概述

生产环境中管理Spring Boot应用的生命周期至关重要。Spring容器通过ApplicationContext负责所有Bean的创建、初始化和销毁。本文聚焦于生命周期中的销毁阶段,探讨多种关闭Spring Boot应用的方法

关于Spring Boot项目搭建,可参考Spring Boot StarterSpring Boot配置

2. 使用Actuator关闭接口

Spring Boot默认启用所有Actuator接口,但/shutdown接口除外。需添加以下Maven依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

若需安全控制,还需添加:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

application.properties中启用关闭接口:

management.endpoints.web.exposure.include=*
management.endpoint.shutdown.enabled=true
endpoints.shutdown.enabled=true

⚠️ 注意:必须显式暴露需要使用的Actuator接口。上述配置暴露了所有接口,包括/shutdown

通过POST请求关闭应用

curl -X POST localhost:port/actuator/shutdown

其中port为Actuator端口。

3. 关闭应用上下文

直接调用应用上下文的close()方法也能关闭应用:

ConfigurableApplicationContext ctx = new 
  SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run();
System.out.println("Spring Boot应用已启动");
ctx.getBean(TerminateBean.class);
ctx.close();

此操作会销毁所有Bean、释放锁并关闭Bean工厂。通过@PreDestroy注解验证关闭过程:

public class TerminateBean {

    @PreDestroy
    public void onDestroy() throws Exception {
        System.out.println("Spring容器已销毁!");
    }
}

需注册该Bean:

@Configuration
public class ShutdownConfig {

    @Bean
    public TerminateBean getTerminateBean() {
        return new TerminateBean();
    }
}

执行后输出:

Spring Boot应用已启动
Closing AnnotationConfigApplicationContext@39b43d60
DefaultLifecycleProcessor - Stopping beans in phase 0
Unregistering JMX-exposed beans on shutdown
Spring容器已销毁!

⚠️ 踩坑:关闭子上下文不会影响父上下文,因它们生命周期独立。

3.1 关闭当前应用上下文

前例创建了子上下文后关闭。若要关闭当前上下文,可:

  • 调用Actuator的/shutdown接口
  • 自定义关闭接口:
@RestController
public class ShutdownController implements ApplicationContextAware {
    
    private ApplicationContext context;
    
    @PostMapping("/shutdownContext")
    public void shutdownContext() {
        ((ConfigurableApplicationContext) context).close();
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        this.context = ctx;
    }
}

通过ApplicationContextAware获取当前上下文,在接口中调用close()

curl -X POST localhost:port/shutdownContext

✅ 生产环境使用此类接口务必添加安全控制!

4. 使用SpringApplication退出

SpringApplication会向JVM注册关闭钩子确保应用正常退出。Bean可实现ExitCodeGenerator返回特定错误码:

ConfigurableApplicationContext ctx = new SpringApplicationBuilder(Application.class)
  .web(WebApplicationType.NONE).run();

int exitCode = SpringApplication.exit(ctx, new ExitCodeGenerator() {
    @Override
    public int getExitCode() {
        // 返回错误码
        return 0;
    }
});

System.exit(exitCode);

Java 8 Lambda写法:

SpringApplication.exit(ctx, () -> 0);

调用System.exit(exitCode)后程序以指定码退出

Process finished with exit code 0

5. 终止应用进程

通过外部脚本关闭应用。首先让应用上下文将PID写入文件:

SpringApplicationBuilder app = new SpringApplicationBuilder(Application.class)
  .web(WebApplicationType.NONE);
app.build().addListeners(new ApplicationPidFileWriter("./bin/shutdown.pid"));
app.run();

创建shutdown.bat脚本:

kill $(cat ./bin/shutdown.pid)

脚本从shutdown.pid读取进程ID并使用kill命令终止应用。

6. 总结

本文介绍了五种关闭Spring Boot应用的方法,开发者应根据场景选择:

方法 适用场景 优点
/shutdown接口 需HTTP远程控制 简单粗暴,支持外部调用
close()方法 编程式控制 直接操作上下文,适合内部逻辑
SpringApplication.exit() 需传递退出码 与JVM交互,支持错误码传递
终止进程 外部脚本管理 支持重启/启动等完整生命周期控制

✅ 最佳实践:

  • 需远程控制用/shutdown接口
  • 需错误码传递用exit()
  • 脚本管理用PID文件
  • 其他场景用close()足够

完整代码见GitHub项目


原始标题:Shutdown a Spring Boot Application | Baeldung