How can I programmatically shutdown a Spring Boot application without terminating the VM?
In other works, what is the opposite of
new SpringApplication(Main.class).run(args); How can I programmatically shutdown a Spring Boot application without terminating the VM?
In other works, what is the opposite of
new SpringApplication(Main.class).run(args); Closing a SpringApplication basically means closing the underlying ApplicationContext. The SpringApplication#run(String...) method gives you that ApplicationContext as a ConfigurableApplicationContext. You can then close() it yourself.
For example,
@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(); } } Alternatively, you can use the static SpringApplication.exit(ApplicationContext, ExitCodeGenerator...) helper method to do it for you. For example,
@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); } } The simplest way would be to inject the following object where you need to initiate the shutdown
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); } } This works, even done is printed.
SpringApplication.run(MyApplication.class, args).close(); System.out.println("done"); So adding .close() after run()
Explanation:
public ConfigurableApplicationContext run(String... args)Run the Spring application, creating and refreshing a new ApplicationContext. Parameters:
args- the application arguments (usually passed from a Java main method)Returns: a running ApplicationContext
and:
void close()Close this application context, releasing all resources and locks that the implementation might hold. This includes destroying all cached singleton beans. Note: Does not invoke close on a parent context; parent contexts have their own, independent lifecycle.This method can be called multiple times without side effects: Subsequent close calls on an already closed context will be ignored.
So basically, it will not close the parent context, that's why the VM doesn't quit.
SpringApplication.exit(appContext, () -> returnCode).SpringApplication.run(MyApplication.class, args), then there is no parent context. There's only one context, the context created and returned by run, which you then immediately close. @Michael is right. This won't work for programs that need to do anything after the Spring context is initialized, which is most programs.This will make sure that the SpringBoot application is closed properly and the resources are released back to the operating system,
@Autowired private ApplicationContext context; @GetMapping("/shutdown-app") public void shutdownApp() { int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0); System.exit(exitCode); } System.exit(exitCode) in my application, otherwise spring boot was restartingIn the application you can use SpringApplication. This has a static exit() method that takes two arguments: the ApplicationContext and an ExitCodeGenerator:
i.e. you can declare this method:
@Autowired public void shutDown(ExecutorServiceExitCodeGenerator exitCodeGenerator) { SpringApplication.exit(applicationContext, exitCodeGenerator); } Inside the Integration tests you can achieved it by adding @DirtiesContext annotation at class level:
@DirtiesContext(classMode=ClassMode.AFTER_CLASS) - The associated ApplicationContext will be marked as dirty after the test class.@DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) - The associated ApplicationContext will be marked as dirty after each test method in the class.i.e.
@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 { ...