1. 概述

在本教程中,我们将介绍如何以编程方式重启一个 Spring Boot 应用

在某些场景下,重启应用是非常有用的:

  • 在运行时更改某些参数后重新加载配置文件
  • 动态切换当前激活的 profile
  • 由于某种原因需要重新初始化整个应用上下文

虽然本文重点在于介绍如何重启 Spring Boot 应用,但我们也有一个非常不错的教程讲解 Spring Boot 应用的关闭,有兴趣可以参考。

接下来,我们来看几种实现 Spring Boot 应用重启的方式。

2. 通过创建新的上下文实现重启

我们可以通过关闭当前应用上下文并从头创建一个新的上下文来实现重启。这种方式虽然看起来简单粗暴,但有几个细节需要注意,否则容易踩坑。

来看一个在 Spring Boot 应用的 main 方法中实现的例子:

@SpringBootApplication
public class Application {

    private static ConfigurableApplicationContext context;

    public static void main(String[] args) {
        context = SpringApplication.run(Application.class, args);
    }

    public static void restart() {
        ApplicationArguments args = context.getBean(ApplicationArguments.class);

        Thread thread = new Thread(() -> {
            context.close();
            context = SpringApplication.run(Application.class, args.getSourceArgs());
        });

        thread.setDaemon(false);
        thread.start();
    }
}

如上代码所示,必须在一个非守护线程中重新创建上下文,否则调用 context.close() 会触发 JVM 关闭流程,导致应用直接退出。因为 JVM 在退出时不会等待守护线程执行完毕。

接下来,我们添加一个 REST 接口用于触发重启操作:

@RestController
public class RestartController {
    
    @PostMapping("/restart")
    public void restart() {
        Application.restart();
    } 
}

然后,我们可以通过如下命令调用接口来重启应用:

curl -X POST localhost:port/restart

⚠️ 当然,如果在真实项目中添加这样的接口,一定要记得做好权限控制,否则谁都能重启你的服务就危险了。

3. 使用 Actuator 的 Restart Endpoint

另一种重启方式是使用 Spring Boot Actuator 提供的内置 RestartEndpoint

首先,我们需要添加相关的 Maven 依赖:

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

接着,在 application.properties 中启用 restart endpoint:

management.endpoint.restart.enabled=true

配置完成后,我们就可以在服务中注入 RestartEndpoint 来使用它了:

@Service
public class RestartService {
    
    @Autowired
    private RestartEndpoint restartEndpoint;
    
    public void restartApp() {
        restartEndpoint.restart();
    }
}

这种方式的好处是:一行代码搞定重启,非常简洁。

不过也有缺点:它依赖于 Spring Cloud 的相关组件。如果你的项目中并没有使用这些组件,为了一个重启功能引入这么多依赖可能不太划算。这种情况下,建议还是使用上一节中的手动重启方式,代码量也不多,反而更轻量。

4. 刷新应用上下文

在某些情况下,我们可能会想到调用应用上下文的 refresh 方法来实现重新加载。

听起来很美好,但现实是:只有部分上下文类型支持多次 refresh。比如:

  • FileSystemXmlApplicationContext
  • GroovyWebApplicationContext

而 Spring Boot 默认使用的是 GenericApplicationContext,它并不支持多次调用 refresh(),如果强行调用会抛出如下异常:

java.lang.IllegalStateException: GenericApplicationContext does not support multiple refresh attempts:
just call 'refresh' once

❌ 因此,这种方式在大多数 Spring Boot Web 应用中是行不通的。

而且即使上下文支持多次 refresh,也不推荐使用,因为 refresh 方法本身是框架内部用于初始化上下文的,暴露出来并不是为了业务层调用。

5. 总结

本文我们介绍了几种在 Spring Boot 应用中实现编程式重启的方法:

手动重启(创建新上下文):轻量、灵活,适用于大多数场景
Actuator 的 RestartEndpoint:简单粗暴,但需要引入额外依赖
调用 refresh 方法:不适用于大多数情况,容易踩坑

示例代码可以在 GitHub 上找到。


原始标题:Programmatically Restarting a Spring Boot Application