Spring 优雅关闭时出现 BeanCreationNotAllowedException。

huangapple go评论110阅读模式
英文:

Getting BeanCreationNotAllowedException on spring graceful shutdown

问题

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?

英文:

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?

答案1

得分: 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.

英文:

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.

答案2

得分: 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 {

private long shutdownDelayInMilliseconds = 3000;

@Override
public void onApplicationEvent(ContextClosedEvent event) {

   // here I can trigger any additional cleanup

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

}

英文:

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();
        }

    }
}

huangapple
  • 本文由 发表于 2023年8月5日 04:26:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76838936.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定