2021-06-04

本文介绍如何在Spring项目中实现动态控制Cron定时任务,包括添加、删除和更新任务。通过自定义的`CronTaskRegistrar`类,结合`TaskScheduler`和`@EnableScheduling`,实现了对定时任务的灵活管理。同时提供了`SchedulingRunnable`类用于执行具体的任务,以及`SpringContextUtils`帮助类获取Bean。此外,还展示了如何通过Controller层接口进行任务的增删改查操作,以及状态切换。

动态控制Cron定时任务

针对应用中需求的大量定时任务,实现灵活有效的控制,自己已经在项目中,希望对小伙伴们有用!

 ![

废话不多直接教你怎么拿来就能用

  Spring提供的CronTask定时任务注册类
@Component
public class CronTaskRegistrar implements DisposableBean {

    /**
     * 缓存
     */
    private final Map < Runnable, ScheduledTask > scheduledTasks = new ConcurrentHashMap<>(16);

    @Autowired
    private TaskScheduler taskScheduler;

    /**
     * 添加一个定时任务 其核心就是靠spring提供的 CronTask 类来实现
     *
     * @param task
     * @param cronExpression
     */
    public void addCronTask(Runnable task, String cronExpression) {
        addCronTask(new CronTask(task, cronExpression));
    }

    public void addCronTask(CronTask cronTask) {
        if (cronTask != null) {
            Runnable task = cronTask.getRunnable();
            if (this.scheduledTasks.containsKey(task)) {
                removeCronTask(task);
            }
            this.scheduledTasks.put(task, scheduleCronTask(cronTask));
        }
    }

    public void removeCronTask(Runnable task) {
        ScheduledTask scheduledTask = this.scheduledTasks.remove(task);
        if (scheduledTask != null)
            scheduledTask.cancel();
    }

    public ScheduledTask scheduleCronTask(CronTask cronTask) {
        ScheduledTask scheduledTask = new ScheduledTask();
        scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());
        return scheduledTask;
    }

    @Override
    public void destroy() {
        for (ScheduledTask task : this.scheduledTasks.values()) {
            task.cancel();
        }
        this.scheduledTasks.clear();
    }
}
定时任务多线程配置类:
@EnableScheduling // 开启定时任务
@EnableAsync // EnableAsync注解的意思是可以异步执行,就是开启多线程的意思
public class SchedulingConfig {

    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        // 定时任务执行线程池核心线程数
        taskScheduler.setPoolSize(100);
        taskScheduler.setRemoveOnCancelPolicy(true);
        // 线程池任务调度
        taskScheduler.setThreadNamePrefix("MyTaskSchedulerThreadPool-");
        return taskScheduler;
    }
}
定时任务运行类

    private static final Logger logger = LoggerFactory.getLogger(SchedulingRunnable.class);

    private String beanName;

    private String methodName;

    private Integer frequency;

    private Object[] params;

    public SchedulingRunnable(String beanName, String methodName, Integer frequency) {
        this(beanName, methodName, frequency, null);
    }

    public SchedulingRunnable(String beanName, String methodName, Integer frequency, Object... params) {
        this.beanName = beanName;
        this.methodName = methodName;
        this.frequency = frequency;
        this.params = params;
    }

    @Override
    public void run() {
        // logger.info("定时任务开始执行 - bean:{},方法:{},参数:{}", beanName, methodName, params);
        long startTime = System.currentTimeMillis();
        try {
            Object target = SpringContextUtils.getBean(beanName);

            Method method = null;
            if (null != params && params.length > 0) {
                Class < ? >[] paramCls = new Class[params.length];
                for (int i = 0; i < params.length; i++) {
                    paramCls[i] = params[i].getClass();
                }
                method = target.getClass().getDeclaredMethod(methodName, paramCls);
            }
            else {
                method = target.getClass().getDeclaredMethod(methodName);
            }

            ReflectionUtils.makeAccessible(method);
            if (null != params && params.length > 0) {
                method.invoke(target, params);
            }
            else {
                method.invoke(target);
            }
        }
        catch (Exception ex) {
            ex.printStackTrace();
            logger.error(String.format("定时任务执行异常 - bean:%s,方法:%s ", beanName, methodName), ex);
        }
        long times = System.currentTimeMillis() - startTime;
        long frequencyTime = (System.currentTimeMillis() - startTime) / 1000;// 执行时间
        if ((int) frequencyTime > frequency) {// 如果执行时间大于设置的时间
            logger.info("定时任务执行结束 - bean:{},方法:{},参数:{},耗时:{} 毫秒,预设时间数据分析:{}", beanName, methodName, params, times,
                    "超时");
        }
        // else {
        // logger.info("定时任务执行结束 - bean:{},方法:{},参数:{},耗时:{} 毫秒,预设时间数据分析:{}", beanName, methodName, params, times,
        // "正常");
        // }

    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        SchedulingRunnable that = (SchedulingRunnable) o;
        if (params == null) {
            return beanName.equals(that.beanName) && methodName.equals(that.methodName) && that.params == null;
        }

        return beanName.equals(that.beanName) && methodName.equals(that.methodName) && params.equals(that.params);
    }

    @Override
    public int hashCode() {
        if (params == null) {
            return Objects.hash(beanName, methodName);
        }

        return Objects.hash(beanName, methodName, params);
    }
}

这个也需要,差点忘了

@Component
public class SpringContextUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

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

    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }

}

定时任务控制器

public class SysJobRunner implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(SysJobRunner.class);

    @Value("${scheduled.enable}")
    private String scheduledEnable;

    @Autowired
    private SysJobPOMapper sysJobMapper;

    @Autowired
    private CronTaskRegistrar cronTaskRegistrar;

    @Override
    public void run(String... args) {
        // 如果是真的就不执行了
        if (Boolean.parseBoolean(scheduledEnable))
            return;

        // 初始加载数据库里状态为正常的定时任务
        List < SysJobPO > jobList = sysJobMapper.getSysJobListByStatus(SysJobStatus.NORMAL.index());
        if (!CollectionUtils.isEmpty(jobList)) {
            for (SysJobPO job : jobList) {
                SchedulingRunnable task = new SchedulingRunnable(job.getBeanName(), job.getMethodName(),
                        job.getFrequency());
                cronTaskRegistrar.addCronTask(task, job.getCronExpression());
            }
            logger.info("定时任务已加载完毕...");
        }
    }
}
状态控制器
public enum SysJobStatus {
    NORMAL("正常", 1), SUSPEND("暂停", 0);

    private String desc;

    private Integer index;

    private SysJobStatus(String desc, Integer index) {
        this.desc = desc;
        this.index = index;
    }

    public String desc() {
        return this.desc;
    }

    public Integer index() {
        return this.index;
    }
}
Controller层控制类
public class TaskController {
    @Autowired
    private SysJobPOMapper sysJobMapper;

    @Autowired
    private CronTaskRegistrar cronTaskRegistrar;

    @PostMapping("/addTask")
    public String addTask(SysJobPO sysJob) {
        sysJob.setCreateTime(new Date());
        boolean success = sysJobMapper.addSysJob(sysJob);
        if (!success)
            return "新增失败";
        else {
            if (sysJob.getJobStatus().equals(SysJobStatus.NORMAL.index())) {
                SchedulingRunnable task = new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(),
                        sysJob.getFrequency());
                // 注册定时任务
                cronTaskRegistrar.addCronTask(task, sysJob.getCronExpression());
            }
        }
        return "SUCCESS";
    }

    /**
     * 修改定时任务(只能先停,修改状态然后再开启)
     *
     * @param sysJob
     * @return
     */
    @PostMapping("/updateTask")
    public String updateTaskJob(SysJobPO sysJob) {
        SysJobPO existJob = sysJobMapper.findTaskJobByJobId(sysJob.getJobId());
        sysJob.setUpdateTime(new Date());
        boolean success = sysJobMapper.updateTaskJob(sysJob);
        if (!success) {
            return "修改失败";
        }
        else {
            // 1. 先删除原来的定时任务(Map缓存)(获取到的参数)
            if (existJob.getJobStatus().equals(SysJobStatus.NORMAL.index())) {
                SchedulingRunnable task = new SchedulingRunnable(existJob.getBeanName(), existJob.getMethodName(),
                        existJob.getFrequency());
                cronTaskRegistrar.removeCronTask(task);
            }
            // 2. 新增定时任务(放到Map缓存中)(穿进来的参数)
            if (sysJob.getJobStatus().equals(SysJobStatus.NORMAL.index())) {
                SchedulingRunnable task = new SchedulingRunnable(sysJob.getBeanName(), sysJob.getMethodName(),
                        existJob.getFrequency());
                cronTaskRegistrar.addCronTask(task, sysJob.getCronExpression());
            }
        }
        return "SUCCESS";
    }

    /**
     * 删除某个定时任务
     */
    @GetMapping("/deleteTask")
    public String deleteTask(Integer jobId) {
        SysJobPO existJob = sysJobMapper.findTaskJobByJobId(jobId);
        boolean success = sysJobMapper.deleteTaskJobByJobId(jobId);
        if (!success)
            return "删除失败";
        else {
            if (existJob.getJobStatus().equals(SysJobStatus.NORMAL.index())) {
                SchedulingRunnable task = new SchedulingRunnable(existJob.getBeanName(), existJob.getMethodName(),
                        existJob.getFrequency());
                // 删除定时任务
                cronTaskRegistrar.removeCronTask(task);
            }
        }
        return "SUCCESS";
    }

    /**
     * 启,停定时任务的状态切换
     */

    @GetMapping("/trigger")
    public String triggerTaskJob(Integer jobId) {
        SysJobPO existJob = sysJobMapper.findTaskJobByJobId(jobId);
        // 1.如果原先是启动状态 ,那么就停止吧(从Map缓存中删除, 并将表中状态置为0)
        if (existJob.getJobStatus().equals(SysJobStatus.NORMAL.index())) {
            SchedulingRunnable task = new SchedulingRunnable(existJob.getBeanName(), existJob.getMethodName(),
                    existJob.getFrequency());
            cronTaskRegistrar.removeCronTask(task);
            existJob.setJobStatus(0);
            sysJobMapper.updateTaskJob(existJob);
        }
        else {
            SchedulingRunnable task = new SchedulingRunnable(existJob.getBeanName(), existJob.getMethodName(),
                    existJob.getFrequency());
            cronTaskRegistrar.addCronTask(task, existJob.getCronExpression());
            existJob.setJobStatus(1);
            sysJobMapper.updateTaskJob(existJob);
        }
        return "SUCCESS";
    }

}
取消定时任务
public final class ScheduledTask {

    @SuppressWarnings("rawtypes")
    volatile ScheduledFuture future;

    @SuppressWarnings("rawtypes")
    public void cancel() {
        ScheduledFuture future = this.future;
        if (future != null) {
            future.cancel(true);
        }
    }
}
实体类也扔一下吧,因为有的小伙伴特别懒
@Entity
@Table(name = "sys_job_po")
public class SysJobPO implements java.io.Serializable {

    private static final long serialVersionUID = 4769112479059268023L;

    /**
     * 任务ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长使用
    private Integer jobId;

    /**
     * bean名称 , 如示例中的: demoTask
     */
    private String beanName;

    /**
     * 方法名称
     */
    private String methodName;

    /**
     * cron表达式
     */
    private String cronExpression;

    /**
     * 状态(1正常 0暂停)
     */
    private Integer jobStatus;

    /**
     * 备注
     */
    private String remark;

    /**
     * 创建时间
     */
    private Date createTime;
拿来主义盛行,给你喂到嘴里
public interface SysJobPOMapper {
    boolean deleteTaskJobByJobId(Integer jobId);

    boolean addSysJob(SysJobPO record);

    SysJobPO findTaskJobByJobId(Integer jobId);

    List < SysJobPO > getSysJobListByStatus(Integer jobStatus);

    List < SysJobPO > triggerTaskBeanName(String beanName);

    boolean updateTaskJob(SysJobPO record);
}

备注:

如果你不懂,可以留言,或者代码不全,也可以留言或者私信我

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值