跳至主要內容

动态创建定时任务

chanchaw大约 2 分钟languagejava

概述

通过继承 ScheduledTaskRegistrar 制作动态增删定时任务的工具类,第一次使用在 showa 项目中

源码

下面是动态工具类

package com.xdf.showa.utils;

import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class DynamicScheduledTaskRegistrar extends ScheduledTaskRegistrar {
    private static final Logger logger = LoggerFactory.getLogger("dynamicScheduleTask");
    private final Map<String, ScheduledTask> scheduledTaskMap = new LinkedHashMap<>(16);

    public DynamicScheduledTaskRegistrar(){
        super();
        // 第二种实现方案
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(8);// 执行定时任务的线程数量
        taskScheduler.setRemoveOnCancelPolicy(true);// 被取消的任务会被自动移除,防止尸位素餐
        taskScheduler.setThreadNamePrefix("dynamic-scheduled-task-");// 定时任务线程池中线程名称的前缀
        taskScheduler.initialize();
        this.setScheduler(taskScheduler);
    }
    /**
     * 新增任务
     * @param taskName
     * @param cron
     * @param runnable
     */
    public Boolean addCronTask(String taskName,String cron,Runnable runnable){
        if(scheduledTaskMap.containsKey(taskName)){
            logger.error("定时任务["+ taskName+"]已存在,添加失败");
            return Boolean.FALSE;
        }
        CronTask cronTask = new CronTask(runnable,cron);
        ScheduledTask scheduledTask = this.scheduleCronTask(cronTask);
        scheduledTaskMap.put(taskName,scheduledTask);
        logger.info("定时任务["+taskName+","+cron+"]新增成功");
        return Boolean.TRUE;
    }

    /**
     * 删除指定名称的定时任务
     * @param taskName
     */
    public void cancelCronTask(String taskName){
        ScheduledTask scheduledTask = scheduledTaskMap.get(taskName);
        if(null != scheduledTask){
            scheduledTask.cancel();
            scheduledTaskMap.remove(taskName);
        }
        logger.info("定时任务["+taskName+"]删除成功");
    }

    // 销毁所有定时任务
    @Override
    public void destroy() {
        super.destroy();
        scheduledTaskMap.values().forEach(ScheduledTask::cancel);
        logger.info("已销毁所有定时任务");
    }

    // 返回所有任务的名称构成的字符串集合
    public List<String> getAllTaskName(){
        return scheduledTaskMap.keySet().stream().collect(Collectors.toList());
    }
}

下面介绍使用方法

private final DynamicScheduledTaskRegistrar dynamicScheduledTaskRegistrar = new DynamicScheduledTaskRegistrar();
String taskName = "定时任务01";
String cron = "01 45 10 * * ?";// 每天10:45:01执行一次
dynamicScheduledTaskRegistrar.addCronTask(taskName,cron,() -> { System.out.println("执行了定时任务") });

也可以纳入 spring 容器后通过自动注入使用,在项目中新增自定义配置类创建动态任务注册类对象并纳入 spring 容器

package com.xdf.wxpad.config;

import com.xdf.wxpad.util.DynamicScheduledTaskRegistrar;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CustomizeConfigure {
    // 动态定时任务工具纳入spring容器
    @Bean("dynamicScheduledTaskRegistrar")
    public DynamicScheduledTaskRegistrar initTaskRegistrar(){
        return new DynamicScheduledTaskRegistrar();
    }
}

注意

如代码 dynamicScheduledTaskRegistrar.addCronTask(taskName,cron,() -> remindPriorityMain(priorityMain)); 执行定时任务时传入了对象 priorityMain 可能会有状态过期的问题。比如在创建定时任务时对象 priorityMain 的进度是正在处理,而在第二天执行任务时该任务已经在前一天设置 验收合格 了,此时不应该再发送邮件通知担当者,所以定时任务的传入参数应该是任务的主键,在执行任务时使用主键重新查询最新的业务数据,拿到最新状态后再决定是否要发送邮件。