一尘不染

如何在web应用程序中的所有其他bean被销毁之前关闭Spring任务执行器/调度程序池?

spring

在Spring Web应用程序中,我有几个DAO和服务层bean。一个服务层bean带有注释的@Async / @Scheduled方法。这些方法取决于其他(自动装配)的bean。我已经在XML中配置了两个线程池:

<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
     <property name="corePoolSize" value="2" />
     <property name="maxPoolSize" value="5" />
     <property name="queueCapacity" value="5" />
     <property name="waitForTasksToCompleteOnShutdown" value="true" />
     <property name="rejectedExecutionHandler">
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/>
        </property>
    </bean>

<bean id="taskScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
     <property name="poolSize" value="10" />
     <property name="waitForTasksToCompleteOnShutdown" value="true" />
     <property name="rejectedExecutionHandler">
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/>
        </property>
    </bean>

    <task:annotation-driven executor="taskExecutor" scheduler="taskScheduler"/>

一切正常。我的问题是我无法完全关闭任务池才能正常工作。这些任务在数据库和文件系统上运行。当我停止Web应用程序时,它需要一些时间才能停止。这表明该waitForTasksToCompleteOnShutdown属性有效。但是,我在日志中得到IllegalStateExceptions,指示某些bean已被销毁,但某些工作任务线程仍在执行,并且由于其依赖关系被销毁而失败。

JIRA问题可能与此有关:SPR-5387

我的问题是:有没有办法告诉Spring最后初始化任务执行者/调度程序bean,还是有办法告诉Spring首先销毁它们?

我的理解是破坏是以相反的顺序发生的。因此,最后初始化的bean将首先被销毁。如果线程池bean首先被销毁,则所有当前正在执行的任务将完成,并且仍可以访问相关的bean。

我还尝试在引用我的具有@Async和@Scheduled批注的服务bean的线程池上使用depends-on属性。好像它们从未执行过,并且我没有遇到上下文初始化错误。我假设带注释的服务bean某种程度上需要首先初始化这些线程池,如果我使用依赖关系,我会颠倒顺序并使它们不起作用。


阅读 620

收藏
2020-04-13

共1个答案

一尘不染

两种方式:

  1. 有一个豆子工具ApplicationListener。 onApplicationEvent()将在上下文和所有bean被销毁之前被调用。

  2. 有一个bean实现Lifecycle或SmartLifecycle。 stop()将在上下文和所有bean被销毁之前被调用。

无论哪种方式,你都可以在Bean销毁机制发生之前关闭任务。

例如:

@Component
public class ContextClosedHandler implements ApplicationListener<ContextClosedEvent> {
    @Autowired ThreadPoolTaskExecutor executor;
    @Autowired ThreadPoolTaskScheduler scheduler;

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        scheduler.shutdown();
        executor.shutdown();
    }       
}
2020-04-13