以编程方式关闭Spring Boot应用程序

158

我如何以编程方式关闭一个Spring Boot应用程序,而不终止VM?

换句话说,什么是相反的操作?

new SpringApplication(Main.class).run(args);

1
好的!调用close()应该可以完成任务。 - Axel Fontaine
可能是如何正确关闭Spring Boot应用程序?的重复问题。 - Anand Varkey Philips
@AnandVarkeyPhilips 不是的,这个是关于API的,另一个是关于运维人员执行的一种方式。 - Axel Fontaine
1
好的,那个问题链接可能会帮助其他人。你想让我删除上面的评论吗? - Anand Varkey Philips
5个回答

144

关闭一个SpringApplication,基本上意味着关闭底层的ApplicationContext。方法SpringApplication#run(String...)将该ApplicationContext作为ConfigurableApplicationContext提供给您。然后您可以自行关闭它(close())

例如,

@SpringBootApplication
public class Example {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
        // ...determine it's time to shut down...
        ctx.close();
    }
}

或者,您可以使用static SpringApplication.exit(ApplicationContext, ExitCodeGenerator...)辅助方法来为您完成。例如,

@SpringBootApplication
public class Example {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
        // ...determine it's time to stop...
        int exitCode = SpringApplication.exit(ctx, new ExitCodeGenerator() {
            @Override
            public int getExitCode() {
                // no errors
                return 0;
            }
        });

        // or shortened to
        // int exitCode = SpringApplication.exit(ctx, () -> 0);

        System.exit(exitCode);
    }
}

140

最简单的方法是在需要启动关闭的地方注入以下对象

ApplicationShutdownManager.java

import org.springframework.context.ApplicationContext;
import org.springframework.boot.SpringApplication;

@Component
class ApplicationShutdownManager {

    @Autowired
    private ApplicationContext appContext;

    /*
     * Invoke with `0` to indicate no error or different code to indicate
     * abnormal exit. es: shutdownManager.initiateShutdown(0);
     **/
    public void initiateShutdown(int returnCode){
        SpringApplication.exit(appContext, () -> returnCode);
    }
}

46

这个有效,即使已经打印出来。

  SpringApplication.run(MyApplication.class, args).close();
  System.out.println("done");
run()方法后添加.close()
说明:

public ConfigurableApplicationContext run(String... args)

运行Spring应用程序,创建并刷新新的ApplicationContext。参数:

args - 应用程序参数(通常从Java main方法传递)

返回值: 正在运行的ApplicationContext

void close()关闭此应用程序上下文,释放实现可能持有的所有资源和锁定。这包括销毁所有缓存的单例bean。注意:不会在父上下文上调用close;父上下文具有它们自己的独立生命周期。

可以多次调用此方法而没有副作用: 对于已经关闭的上下文的后续关闭调用将被忽略。

基本上,它不会关闭父上下文,这就是为什么VM不退出的原因。

2
提醒一下,这个解决方案适用于像批处理这样的短暂进程,但不要在Spring MVC应用程序中使用它。应用程序在启动后会立即关闭。 - Michaël COLL
@MichaelCOLL 这个问题是关于如何编程式地关闭一个Spring Boot应用程序,而不考虑其类型。它同样适用于Spring MVC。 - ACV
1
@ACV 你说得对,它可以工作,而且非常好。但是对于必须保持运行的应用程序(例如Spring MVC应用程序),我认为这不是正确的做法。在我的情况下,我使用了SpringApplication.exit(appContext, () -> returnCode) - Michaël COLL
1
你在最后一行提到的VM是指什么?如果你使用SpringApplication.run(MyApplication.class, args)启动你的Spring Boot应用程序,那么就没有父上下文。只有一个上下文,即由run创建和返回的上下文,然后你立即将其关闭。@Michael是正确的。这对于需要在Spring上下文初始化后执行任何操作的程序(大多数程序)是不起作用的。 - Savior
你认为有哪个父上下文来自哪里?run 创建了一个单独的 ApplicationContext,没有任何层次结构。无论它是 Web 应用程序还是其他应用程序,在关闭该上下文时,将销毁其 bean(并关闭线程池),并且假设程序除与上下文无关的其他事情外不执行任何其他操作,则会退出。我认为 @MichaelCOLL 的观点是,最好捕获由 run 返回的上下文,并在准备退出应用程序时基于某些辅助信号调用 close - Savior
显示剩余2条评论

15

这将确保SpringBoot应用程序被正确关闭并释放资源回到操作系统中,

@Autowired
private ApplicationContext context;

@GetMapping("/shutdown-app")
public void shutdownApp() {

    int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0);
    System.exit(exitCode);
}

1
如果在我的应用程序中需要使用System.exit(exitCode),否则Spring Boot将会重新启动。 - Jonas D

3
在应用程序中,您可以使用SpringApplication。它有一个静态的exit()方法,接受两个参数: ApplicationContextExitCodeGenerator
例如,您可以声明此方法:
@Autowired
public void shutDown(ExecutorServiceExitCodeGenerator exitCodeGenerator) {
    SpringApplication.exit(applicationContext, exitCodeGenerator);
}

在集成测试中,您可以通过在类级别添加@DirtiesContext注释来实现:
  • @DirtiesContext(classMode=ClassMode.AFTER_CLASS) - 在测试类之后,关联的ApplicationContext将被标记为脏状态。
  • @DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) - 在类中的每个测试方法之后,关联的ApplicationContext将被标记为脏状态。
例如:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {Application.class},
    webEnvironment= SpringBootTest.WebEnvironment.DEFINED_PORT, properties = {"server.port:0"})
@DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ApplicationIT {
...

好的。我应该从哪里获取ExecutorServiceExitCodeGenerator?如果它是一个bean,你能展示一下创建代码片段吗(以及它是从哪个类创建的)?在哪个类中应该放置shutDown(ExecutorServiceExitCodeGenerator exitCodeGenerator)方法? - Vlad G.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接