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:
- 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.
- 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;
}
}
start()
method will start the queue listeners.
stop()
method will shut down the queue listeners.
isRunning()
method will provide the current status.
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.