155

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);
Axel Fontaine
  • 34,542
  • 16
  • 106
  • 137

5 Answers5

143

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);
    }
}
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
138

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);
    }
}
snovelli
  • 5,804
  • 2
  • 37
  • 50
46

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.

ACV
  • 9,964
  • 5
  • 76
  • 81
  • 2
    Just a reminder this solution works for short lived process like batch, but don't use this on Spring MVC applications. The application just shutdown after booting. – Michaël COLL Nov 08 '19 at 13:54
  • @MichaelCOLL the question is about how to programatically shutdown a spring boot app regardless of type. It also works for Spring MVC – ACV Nov 08 '19 at 16:51
  • 1
    @ACV You're right it works, it works very well. But for an app that must stay up (like Spring MVC app), I think, it's not the good way of doing this. In my case, I've used `SpringApplication.exit(appContext, () -> returnCode)`. – Michaël COLL Nov 10 '19 at 16:55
  • 1
    What VM are you referring to in your last line? If you're starting your Spring Boot application with `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. – Savior Jun 22 '20 at 17:32
  • @Savior JVM. There is a parent context. Here we're talking about how to shut down a Spring boot application. You normally don't shut down web applications this way. So this mechanism is usually used for short lived applications that do something then need to stop. By default, Spring boot will just keep on running even after you finished your batch processing so that's where you would want to use this mechanism. – ACV Jun 24 '20 at 12:24
  • Which parent context do you think there is, from where? `run` creates a single `ApplicationContext`, there's no hierarchy. Whether it's a web application or not, closing that context will destroy its beans (and shutdown thread pools) and, assuming the program doesn't do anything else unrelated to the context, will quit. I think the point `@MichaelCOLL was trying to make is that you're better off capturing the context returned by `run` and calling `close` when you're ready to exit the application, based on some secondary signal. – Savior Jun 24 '20 at 13:27
  • Also, even if there was a parent context, you'd be shutting it down and destroying all of its beans, which wouldn't be much use to the other context, effectively breaking the application. – Savior Jun 24 '20 at 13:29
13

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);
}
sam
  • 1,800
  • 1
  • 25
  • 47
  • 1
    +! as required the `System.exit(exitCode)` in my application, otherwise spring boot was restarting – Jonas D Feb 24 '21 at 09:54
3

In 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 {
...
magiccrafter
  • 5,175
  • 1
  • 56
  • 50
  • Ok. Where am I supposed to get ExecutorServiceExitCodeGenerator? If it is a bean, can you show the creation snippet code (and from which class it is created)? In which class the method shutDown(ExecutorServiceExitCodeGenerator exitCodeGenerator) should be put? – Vlad G. Mar 26 '19 at 18:19