0

My spring boot application uses spring boot graceful shutdown setting. In the app I'm having queue listeners in the separate threads. Everything gets shut down correctly except that every now and then I get error BeanCreationNotAllowedException Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!). It happens because in my listener I'm calling beanFactory.getBean but the bean destroy was already initiated, hence the error.

Is there any way to make spring boot wait longer with destroying of the beans?

user2455862
  • 585
  • 10
  • 26

2 Answers2

0

This issue arises when a shutdown signal is received and Spring is in the process of closing down the ApplicationContext and destroying the beans. However, in parallel, there might be some tasks in other threads that still require beans from the BeanFactory.

One way to handle this situation is to use a SmartLifecycle interface. This interface has a getPhase() method that can be used to control the order in which beans are started and stopped.

To accomplish, you can to the following:

  1. Implement SmartLifecycle interface for the bean that has your queue listeners. It should also include logic in the stop() method to shut down the queue listeners gracefully.
  2. For the getPhase() method, return a negative value (like -1). This will make sure that your listeners get stopped before any other beans.
@Component
public class QueueListenerManager implements SmartLifecycle {

    private volatile boolean running = false;

    @Override
    public void start() {
        running = true;
        // Add logic to start the listeners
    }

    @Override
    public void stop() {
        // Add logic to gracefully shutdown the listeners
        running = false;
    }

    @Override
    public boolean isRunning() {
        return running;
    }

    @Override
    public int getPhase() {
        return -1;
    }
}
  1. start() method will start the queue listeners.
  2. stop() method will shut down the queue listeners.
  3. isRunning() method will provide the current status.
  4. getPhase() method is important as it controls the order of stopping beans. Beans are stopped in descending order of their phase value and in our case it's -1, so it will stop before beans with phase values 0 or greater.

Remember that the beans that your listeners depend on should not implement SmartLifecycle or if they do, they should have phase values greater than or equal to zero to ensure they get destroyed after the listeners. also make sure that your Spring Boot application has graceful shutdown enabled,

server.shutdown=graceful

This will ensure that Spring Boot waits for all the SmartLifecycle beans to stop before proceeding with shutdown.

Akhilesh Pandey
  • 855
  • 5
  • 10
  • Hi! Unfortunately it doesn't work. I can see the lifecycle methods being called and maybe my listener bean is destroyed later but it is still too early. My listener thread wants to use the bean 2 seconds after the shutdown was initiated. – user2455862 Aug 07 '23 at 19:26
0

I made it work. I'm using ApplicationListener and adding Thread.sleep on shutdown. This is executed before spring starts actual shutdown so it delays the destruction of the beans.

@Component
@RequiredArgsConstructor
public class GracefulShutdownListener implements ApplicationListener<ContextClosedEvent> {

    private long shutdownDelayInMiliseconds = 3000;


    @Override
    public void onApplicationEvent(ContextClosedEvent event) {

       // here I can trigger any additional cleanup

        try {
            Thread.sleep(shutdownDelayInMiliseconds);
        } catch (InterruptedException interruptedException) {
            Thread.currentThread().interrupt();
        }

    }
}
user2455862
  • 585
  • 10
  • 26