4

Is there a clean way to detect when a spring-boot application is stopped and perform some action before? Kind of CommandLineRunner for stopping a service

Thanks in advance

IKane
  • 275
  • 2
  • 8
  • 17
  • You can have a look at e.g. http://docs.spring.io/spring/docs/4.1.6.RELEASE/spring-framework-reference/html/beans.html#beans-java. There you could call registerShutdownHook() on AbstractApplicationContext in non-web applications. – sven.kwiotek Jun 16 '16 at 08:32

4 Answers4

4

Similar to ApplicationReadyEvent you can use ContextClosedEvent:

@Component
public class ContextClosedEventListener {

    @EventListener(ContextClosedEvent.class)
    public void onContextClosedEvent(ContextClosedEvent contextClosedEvent) {
        System.out.println("ContextClosedEvent occurred at millis: " + contextClosedEvent.getTimestamp());
    }
}
Per Lundberg
  • 3,837
  • 1
  • 36
  • 46
RoBeaToZ
  • 1,113
  • 10
  • 18
3

I've come up with this solution. If you have better one, feel free to share

@Component
public class PortalServiceLifeCycle implements CommandLineRunner {

static final Logger LOGGER = LoggerFactory.getLogger(PortalServiceLifeCycle.class);

@Override
public void run(String... arg0) throws Exception {
    LOGGER.info("###START FROM THE LIFECYCLE###");
}

@PreDestroy
public void onExit() {
    LOGGER.info("###STOP FROM THE LIFECYCLE###");
}
}
IKane
  • 275
  • 2
  • 8
  • 17
2

Don't know if you have resolve this problem perfectly. I meet this issue recently, and have got a solution that a little different.

Firstly, my Spring boot Application is a Tomcat embedded one. (The second method of this issue doesn't depends on the web structure. don't mad, my friend.) In this case, it's naturally to get the idea of catch the stop event by register a listener. I do it like this,

@WebListener
public class HelloListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("HelloListener contextInitialized");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("HelloListener contextDestroyed");
    }
}

and, at the same time, add the annotation @ServletComponentScan on your Application class.

Surely, there are some other ways to register a ServletContextListener, and once you registered it, you can get the stop event in the contextDestroyed function.

BUT, that don't match my issue very much. I must catch the stop event BEFORE the Spring Beans being destroyed. And here comes the second solution.

modify your application main method like the follow:

    SpringApplication application = new SpringApplication(DemoApplication.class);
    application.addListeners(new MyListener());
    application.run(args);

and provide the defination of class MyListener:

class MyListener implements ApplicationListener<ContextClosedEvent>{

    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
        // your code here
    }
}

NOTE: the second solution has nothing to do with Tomcat or other web container. The ContextClosedEvent isn't introduced in the Spring document, but I found it in the source, it's very useful i think.

I will be very glad if this can help some one.

  • from your post, i got the idea to do this `@Component public class ApplicationEventListener implements ApplicationListener { @Override public void onApplicationEvent(ApplicationEvent applicationEvent) { if(applicationEvent instanceof ContextClosedEvent){ // do stuff }` – 1housand Dec 03 '18 at 21:32
1

It depends what you want to do but one thing you could do is have a bean that implements SmartLifecycle and implement the stop method. Whenever the context is being stopped, you'd get a callback. Note that it does not necessarily means that the process is shutting down. If you want to invoke some code when that happens, I'd register a shutdown hook as Sven wrote in a comment.

Stephane Nicoll
  • 31,977
  • 9
  • 97
  • 89