Spring Boot - shutdown and restart application programmatically

Today we will be discussing how to shutdown and restart a Spring application programmatically.

1) New Project

First let's create a simple spring boot app with web dependency so that we can setup an endpoint to trigger the shutdown and restart. Let's head over to  https://start.spring.io/ and choose web and other dependencies that you need

2) Few words about ApplicationContext and ConfigurableApplicationContext

ApplicationContext is the central interface that represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans.  ConfigurableApplicationContext is another interface that extends  ApplicationContext and provides facilities to configure refresh, startup & shutdown of the application and allows registration of custom listeners to respond to those events.

3) Let's code!

3.1) Simple App

Run the application and store ConfigurableApplicationContext in a static variable appCtx so that we can use it later

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class App {

static ConfigurableApplicationContext appCtx;

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

3.2) Shutdown - call ConfigurableApplicationContext.close() 

This closes the application context, releasing all resources and locks that the implementation might hold. This includes destroying all cached singleton beans.

static void shutdown() {
appCtx.close();
}

3.3) Restart

We know how to stop and start the application. To stop, we can simply do:

appCtx.close();

And to start, we can simply call the SpringApplication.run(..)

appCtx = SpringApplication.run(App.class, args);

But the catch here is, appCtx.close() would exit the application and the SpringApplication.run(..) won't be able to run

So, a solution would be to use Daemon Thread to shutdown and start the application. Daemon thread is a type of thread that can run independently in the background(independent of the main thread). 

Thread t = new Thread(() -> {
appCtx.close();
appCtx = SpringApplication.run(...);
});

t.setDaemon(false);
t.start();


3.4) Passing the original program arguments

We may want to use the original arguments that were passed to run method when we restart the application.

 SpringApplication.run(App.class, args);

We can do that by either storing String[] args in a static variable or use ApplicationArguments bean that Spring Boot creates to store the arguments.

var args = appCtx.getBean(ApplicationArguments.class);      //retrieve
appCtx = SpringApplication.run(App.class, args.getSourceArgs()); //pass


3.5) Web endpoints to trigger the shutdown and restart

@RestController
class Ctrl {

@GetMapping("/shutdown")
void shutdown() {
App.shutdown();
}

@GetMapping("/restart")
void restart() {
App.restart();
}
}


4) Complete Example:

package gt.restart;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class App {

static ConfigurableApplicationContext appCtx;

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

static void shutdown() {
if (appCtx.isActive()) {
appCtx.close();
}
}

static void restart() {
var args = appCtx.getBean(ApplicationArguments.class);

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

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

}

@RestController
class Ctrl {

@GetMapping("/shutdown")
void shutdown() {
App.shutdown();
}

@GetMapping("/restart")
void restart() {
App.restart();
}
}


5) Test

Call the endpoints

GET http://localhost:8080/restart

GET http://localhost:8080/shutdown