Spring-Boot

停止使用@Scheduled注解启动的任务

2018-12-14  本文已影响39人  大哥你先走

子曰:小胜靠智,大胜靠德,常胜靠身体。

1 问题描述

在Spring或Spring Boot应用中如何停止使用@Scheduled启动的定时任务?

2 解决思路

在Spring中,使用@Scheduled启动的定时任务,Spring会构造对应的RunnableCallable对象并提交给ThreadPoolTaskScheduler执行,ThreadPoolTaskScheduler执行RunnableCallable会返回一个ScheduledFuture对象,通过ScheduledFuture对象的cancel()方法可以实现定时任务的停止。

3 解决方法

3.1 通过ScheduledAnnotationBeanPostProcessor关闭

ScheduledAnnotationBeanPostProcessorpostProcessBeforeDestruction方法可以停止使用@Scheduled启动的定时任务,该方法主要用于bean被销毁之前停止该bean的所有定时任务。因此,停止任务的另外一个可行方法是销毁bean,此方法过于简单粗暴,不推荐使用。

简单示例代码如下:

@Service
public class ScheduledProcessor implements ApplicationContextAware {
    private ApplicationContext context;
    private ScheduledAnnotationBeanPostProcessor processor;

    @PostConstruct
    public void initProcessor() {
        processor = (ScheduledAnnotationBeanPostProcessor) context.getBean("org.springframework.context.annotation.internalScheduledAnnotationProcessor");
    }

    public void stopTask(Object bean) {
        processor.postProcessBeforeDestruction(bean, null);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }
}

上面的代码主要是通过ApplicationContext获取ScheduledAnnotationBeanPostProcessor并通过postProcessBeforeDestruction方法取消定时任务。

3.2 通过ScheduledFuture取消任务

通过提供自定义的ThreadPoolTaskScheduler实现,并覆写ThreadPoolTaskScheduler执行任务的方法,将返回的ScheduledFuture的对象和bean的映射关系存储到Map,然后通过bean获取ScheduledFuture对象来停止对应的任务。

下面是一段简单的实例代码:

@Service
public class CustomTaskScheduler extends ThreadPoolTaskScheduler {
    private final Map<Object, ScheduledFuture<?>> scheduledTasks = new IdentityHashMap<>();

    void cancelTask(Object identifier) {
        ScheduledFuture future = scheduledTasks.get(identifier);
        if (null != future) {
            System.out.println("future is not null");
            future.cancel(true);
        }
    }

    /**
     * call parent method and store the result Future for cancel task,
     * you can expand other method of you used.
     *
     * @param task   the task need to be executed
     * @param period the time between two continues execute
     * @return the result of task
     */
    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) {
        ScheduledFuture<?> future = super.scheduleAtFixedRate(task, period);

        ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task;
        // Scheduled annotation only can used for no arguments method so hashCode plus method name is unique.
        scheduledTasks.put(runnable.getTarget(), future);

        return future;
    }

    /**
     * call parent method and store the result Future for cancel task,
     * you can expand other method of you used.
     *
     * @param task      the task need to be executed
     * @param startTime the task first executed time
     * @param period    the time between two continues execute
     * @return the result of task
     */
    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) {
        ScheduledFuture<?> future = super.scheduleAtFixedRate(task, startTime, period);

        ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task;
        // Scheduled annotation only can used for no arguments method so hashCode plus method name is unique.
        scheduledTasks.put(runnable.getTarget(), future);
        return future;
    }
}

ScheduledAnnotationBeanPostProcessor只能停止一个bean所有的任务,上面的方法可以精细的控制停止某一个确定的任务,读者可以思考一下如何实现?

总结

在Spring或Spring Boot中取消任务的方法就是通过任务返回的异步结果Future.cancel()实现。可以从这里下载完整的样例代码。

上一篇下一篇

猜你喜欢

热点阅读