SpringBoot自定义cron表达式注册定时任务

这篇具有很好参考价值的文章主要介绍了SpringBoot自定义cron表达式注册定时任务。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

springBoot自定义cron表达式注册定时任务

一、原理

  • 1、使用Spring自带的TaskScheduler注册任务
  • 2、注册后返回:ScheduledFuture,用于取消定时任务
  • 3、注册任务后不会马上取消任务,所以将任务缓存。在需要取消任务的时候调用取消接口取消
  • 4、cron表达式可以由前端或者后端生成。实现中会校验cron表达式
public class TestScheduled {

    /**
     * 1、使用Spring自带的TaskScheduler注册任务
     * 2、注册后返回:ScheduledFuture,用于取消定时任务
     */
    @Resource
    private TaskScheduler taskScheduler;

    public void registrarTask() {
        //具体的任务Runnable(一般使用类实现Runnable接口)
        Runnable taskRunnable = new Runnable() {
            @Override
            public void run() {

            }
        };
        //cron表达式触发器
        CronTrigger trigger = new CronTrigger("0/5 * * * * ?");
        //开启定时任务的真正方法
        ScheduledFuture<?> future = this.taskScheduler.schedule(taskRunnable, trigger);
        //取消定时任务
        future.cancel(true);
    }
}

二、具体实现

1、配置任务调度器

  • 作用:设置:核心线程数:可同时执行任务数;设置线程名称前缀
  • 可以不配置。不配置就默认使用spring自带的
package com.cc.ssd.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

/** TaskScheduler任务调度器配置类
 * @since 2023/4/21 0021
 * @author CC
 **/
@Configuration
public class CronTaskConfig {

    /**
     * 任务调度器自定义配置
     */
    @Bean(name = "taskScheduler")
    public TaskScheduler taskScheduler() {
        // 任务调度线程池
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        // 定时任务执行线程池核心线程数:可同时执行4个任务
        taskScheduler.setPoolSize(4);
        taskScheduler.setRemoveOnCancelPolicy(true);
        // 线程名称前缀
        taskScheduler.setThreadNamePrefix("Cs-ThreadPool-");
        return taskScheduler;
    }

}

2、定时任务注册类

  • 作用:缓存、注册定时任务;还可以查询、删除定时任务
package com.cc.ssd.registrar;

import com.cc.ssd.task.CronTaskFuture;
import com.cc.ssd.task.CronTaskRunnable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/** 注册定时任务:缓存定时任务、注册定时任务到调度中心
 * @author CC
 **/
@Component
public class CronTaskRegistrar implements DisposableBean {

    private static final Logger log = LoggerFactory.getLogger(CronTaskRegistrar.class);

    /**
     * 缓存任务
     * key:具体的任务
     * value:注册定时任务后返回的ScheduledFuture
     */
    private final Map<Runnable, CronTaskFuture> scheduledTasks = new ConcurrentHashMap<>(16);

    /**
     * 使用自定义的任务调度配置
     */
    @Resource(name = "taskScheduler")
    private TaskScheduler taskScheduler;

    /** 获取任务调度配置
     * @return 任务调度配置
     */
    public TaskScheduler getTaskScheduler() {
        return this.taskScheduler;
    }

    /** 新增定时任务1
     *  存在任务:删除此任务,重新新增这个任务
     * @param taskRunnable 执行的具体任务定义:taskRunnable 实现Runnable
     * @param cronExpression cron表达式
     */
    public void addCronTask(Runnable taskRunnable, String cronExpression) {
        //验证cron表达式是否正确
        boolean validExpression = CronExpression.isValidExpression(cronExpression);
        if (!validExpression) {
            throw new RuntimeException("cron表达式验证失败!");
        }
        //获取下次执行时间
        CronExpression parse = CronExpression.parse(cronExpression);
        LocalDateTime next = parse.next(LocalDateTime.now());
        if (Objects.nonNull(next)) {
            //定时任务下次执行的时间
            String format = next.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            log.info("定时任务下次执行的时间:{}", format);
        }

        //封装成 CronTask(cron任务)
        CronTask cronTask = new CronTask(taskRunnable, cronExpression);
        this.addCronTask(cronTask);
    }

    /** 新增定时任务2
     * @param cronTask :<p>CronTask用于在指定时间间隔内执行定时任务。</p>
     *                   <p>它是通过CronTrigger来实现的,CronTrigger是一个基于cron表达式的触发器,</p>
     *                   <p>可以在指定的时间间隔内触发任务执行。</p>
     * @since 2023/4/21 0021
     * @author CC
     **/
    private void addCronTask(CronTask cronTask) {
        if (Objects.nonNull(cronTask)) {
            //1有这个任务,先删除这个任务。再新增
            Runnable task = cronTask.getRunnable();
            String taskId = null;
            if (task instanceof CronTaskRunnable) {
                taskId = ((CronTaskRunnable) task).getTaskId();
            }
            //通过任务id获取缓存的任务,如果包含则删除,然后新增任务
            Runnable taskCache = this.getTaskByTaskId(taskId);
            if (Objects.nonNull(taskCache) && this.scheduledTasks.containsKey(taskCache)) {
                this.removeCronTaskByTaskId(taskId);
            }
            //2注册定时任务到调度中心
            CronTaskFuture scheduledFutureTask = this.scheduleCronTask(cronTask);

            //3缓存定时任务
            this.scheduledTasks.put(task, scheduledFutureTask);

            //todo cc 4可以将任务保存到数据库中……重新启动程序然后加载数据库中的任务到缓存中……

        }
    }

    /** 注册 ScheduledTask 定时任务
     * @param cronTask cronTask
     * @return 注册定时任务后返回的 ScheduledFutureTask
     */
    private CronTaskFuture scheduleCronTask(CronTask cronTask) {
        //注册定时任务后记录的Future
        CronTaskFuture scheduledTask = new CronTaskFuture();
        //开启定时任务的真正方法
        scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());
//        scheduledTask.setThreadLocal(this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger()));
        return scheduledTask;
    }

    /** 获取任务列表
     * @return
     */
    public List<CronTaskRunnable> getScheduledTasks() {
        List<CronTaskRunnable> tasks = new ArrayList<>();

        Set<Runnable> keySet = scheduledTasks.keySet();
        keySet.forEach(key -> {
            CronTaskRunnable task = new CronTaskRunnable();
            if (key instanceof CronTaskRunnable) {
                CronTaskRunnable taskParent = (CronTaskRunnable) key;
                BeanUtils.copyProperties(taskParent, task);
            }
            tasks.add(task);
        });

        return tasks.stream()
                .sorted(Comparator.comparing(CronTaskRunnable::getTaskId))
                .collect(Collectors.toList());
    }

    /** 根据任务id删除单个定时任务
     * @param taskId 任务id
     */
    public void removeCronTaskByTaskId(String taskId) {
        //通过任务id获取任务
        Runnable task = this.getTaskByTaskId(taskId);
        //需要通过任务id获取任务,然后再移除
        CronTaskFuture cronTaskFuture = this.scheduledTasks.remove(task);
        if (Objects.nonNull(cronTaskFuture)) {
            cronTaskFuture.cancel();
        }
    }

    /** 通过任务id获取任务。未查询到返回null
     * @param taskId 任务id
     * @return java.lang.Runnable
     * @since 2023/4/21 0021
     * @author CC
     **/
    private Runnable getTaskByTaskId(String taskId) {
        Assert.notNull(taskId, "任务id不能为空!");
        Set<Map.Entry<Runnable, CronTaskFuture>> entries = scheduledTasks.entrySet();
        //根据任务id获取该任务缓存
        Map.Entry<Runnable, CronTaskFuture> rcf = entries.stream().filter(rf -> {
            Runnable key = rf.getKey();
            String taskId1 = null;
            if (key instanceof CronTaskRunnable) {
                taskId1 = ((CronTaskRunnable) key).getTaskId();
            }
            return taskId.equals(taskId1);
        }).findAny().orElse(null);

        if (Objects.nonNull(rcf)) {
            return rcf.getKey();
        }
        return null;
    }

    /** 删除所有的定时任务
     *     DisposableBean是Spring框架中的一个接口,它定义了一个destroy()方法,
     *     用于在Bean销毁时执行清理工作。
     *     当一个Bean实现了DisposableBean接口时,
     *     Spring容器会在该Bean销毁时自动调用destroy()方法,
     *     以便进行一些清理工作,例如释放资源等。
     *     如果您的Bean需要在销毁时执行一些清理工作,
     *     那么实现DisposableBean接口是一个很好的选择。
     */
    @Override
    public void destroy() {
        //关闭所有定时任务
        for (CronTaskFuture task : this.scheduledTasks.values()) {
            task.cancel();
        }
        //清空缓存
        this.scheduledTasks.clear();

        log.info("取消所有定时任务!");
        //todo cc 修改或删除数据库的任务
    }

}

3、定时任务的执行结果ScheduledFuture

  • 作用:CronTaskFuture类中使用的是ScheduledFuture对象来表示定时任务的执行结果。
package com.cc.ssd.task;

import java.util.Objects;
import java.util.concurrent.ScheduledFuture;

/** CronTaskFuture类中使用的是ScheduledFuture对象来表示定时任务的执行结果。
 *  ——最后ps:也可以不要这个记录类,直接缓存ScheduledFuture对象。
 *  用来记录单独的Future、定时任务注册任务后产生的
 * @author CC
 **/
public final class CronTaskFuture {

    /** 每个线程一个副本
     * 经过测试这里不能使用ThreadLocal
     */
//    private static final ThreadLocal<ScheduledFuture<?>> THREAD_LOCAL = new ThreadLocal<>();

    /** 最后ps:由于ScheduledFuture是线程安全的。这里不用 volatile 或者 ThreadLocal
     *      注册任务后返回的:ScheduledFuture 用于记录并取消任务
     *      这两个都可以不使用。直接给future赋值
     *          volatile:线程之间可见:volatile用于实现多线程之间的可见性和一致性,保证数据的正确性。
     *          ThreadLocal:用于实现线程封闭,保证线程安全
     * 使用建议:
     *      CronTaskFuture类中使用的是ScheduledFuture对象来表示定时任务的执行结果。
     *      ScheduledFuture对象是线程安全的,因此不需要使用volatile关键字来保证多线程同步。
     *      如果需要在多线程中使用线程本地变量,可以使用ThreadLocal。
     *      因此,建议在CronTaskFuture类中使用ScheduledFuture对象,而不是使用volatile或ThreadLocal。
     *      另外,如果需要在Spring容器销毁时执行一些清理操作,可以实现DisposableBean接口,并在destroy()方法中进行清理操作。
     */
    public ScheduledFuture<?> future;
//    public volatile ScheduledFuture<?> future;
//    public void setThreadLocal(ScheduledFuture<?> future){
//        THREAD_LOCAL.set(future);
//    }

    /**
     * 取消当前定时任务
     */
    public void cancel() {
        try {
//            ScheduledFuture<?> future = THREAD_LOCAL.get();
            ScheduledFuture<?> future = this.future;
            if (Objects.nonNull(future)) {
                future.cancel(true);
            }
        } catch (Exception e) {
            throw new RuntimeException("销毁定时任务失败!");
        } finally {
//            THREAD_LOCAL.remove();
        }

    }

}

4、具体的任务。

  • 实现Runable接口
  • 任务处理的方式按照自己的需求去实现即可
package com.cc.ssd.task;

import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/** 具体任务实现
 * @author CC
 * @since 2023/4/21 0021
 */
@Data
public class CronTaskRunnable implements Runnable {

    private static final Logger log = LoggerFactory.getLogger(CronTaskRunnable.class);

    /**
     * 任务id(必须唯一)
     */
    private String taskId;
    /**
     * 任务类型:自定义
     */
    private Integer taskType;
    /**
     * 任务名字
     */
    private String taskName;
    /**
     * 任务参数
     */
    private Object[] params;

    public CronTaskRunnable() {
    }

    public CronTaskRunnable(String taskId, Integer taskType, String taskName, Object... params) {
        this.taskId = taskId;
        this.taskType = taskType;
        this.taskName = taskName;
        this.params = params;
    }

    /** 执行任务
     * @since 2023/4/21 0021
     * @author CC
     **/
    @Override
    public void run() {
        long start = System.currentTimeMillis();

        //具体的任务。
        log.info("\n\t {}号.定时任务开始执行 - taskId:{},taskName:{},taskType:{},params:{}",
                taskType, taskId, taskName, taskType, params);

        //任务处理的方式:
        //todo cc 1就在这里执行:模拟任务
        //todo cc 2开启策略模式,根据任务类型 调度不同的任务
        //todo cc 3使用反射:传来bean名字,方法名字,调用不同的任务
        //todo cc 4开启队列,把要执行的任务放到队列中,然后执行 —— 使用场景:每个任务执行很耗时的情况下使用
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        log.info("\n\t {}号.任务执行完成 - 耗时:{},taskId:{},taskType:{}",
                taskType, System.currentTimeMillis() - start, taskId, taskType);

    }


}

5、测试Controller

package com.cc.ssd.web.controller;

import com.cc.ssd.registrar.CronTaskRegistrar;
import com.cc.ssd.task.CronTaskRunnable;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * @author CC
 * @since 2023/4/21 0021
 */
@RestController
@RequestMapping("/scheduled")
public class TestScheduledController {

    @Resource
    private CronTaskRegistrar cronTaskRegistrar;

    /** 获取任务列表
     * @return java.util.List<com.cc.ssd.task.SchedulingRunnableTask>
     * @since 2023/4/21 0021
     * @author CC
     **/
    @GetMapping
    public List<CronTaskRunnable> getScheduledTasks() {
        return cronTaskRegistrar.getScheduledTasks();
    }

    /** 添加任务
     * @param param param
     * @return java.lang.String
     * @since 2023/4/21 0021
     * @author CC
     **/
    @PostMapping
    public String addCronTask(@RequestBody Map<String, Object> param) {
        //自己拿任务参数的逻辑:可以把每个任务保存到数据库,重新启动任务的同时,加载这些任务到任务调度中心
        String taskId = (String) param.get("taskId");
        Integer taskType = (Integer) param.get("taskType");
        String taskName = (String) param.get("taskName");
        Object params = param.get("params");
        //添加任务参数
        CronTaskRunnable task = new CronTaskRunnable(taskId, taskType, taskName, params);
        //注册任务:cron表达式,可以从传入不一样的
        cronTaskRegistrar.addCronTask(task, "0/5 * * * * ?");
        return "ok";
    }

    /** 根据任务id删除定时任务
     * @param taskId 任务id
     * @return java.lang.String
     * @since 2023/4/21 0021
     * @author CC
     **/
    @DeleteMapping
    public String removeCronTaskByTaskId(@RequestParam String taskId) {
        cronTaskRegistrar.removeCronTaskByTaskId(taskId);
        return "ok";
    }

    /** 删除全部任务
     * @return java.lang.String
     * @since 2023/4/21 0021
     * @author CC
     **/
    @DeleteMapping("/removeAll")
    public String removeCronTask() {
        cronTaskRegistrar.destroy();
        return "ok";
    }

}

6、最后效果

  • 自己用controller去测试一波吧

SpringBoot自定义cron表达式注册定时任务文章来源地址https://www.toymoban.com/news/detail-420470.html

到了这里,关于SpringBoot自定义cron表达式注册定时任务的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Cron在前端的使用,vue与element ui的vue-cron插件的使用及将定时任务cron表达式解析成中文

    Cron在前端的使用,vue与element ui的vue-cron插件的使用及将定时任务cron表达式解析成中文

    执行下面npm命令: npm install vue-cron --save 在想使用cron的vue页面引入以下: import VueCron from ‘vue-cron’ import Vue from ‘vue’ Vue.use(VueCron) 运行 在vue页面“style scoped”中通过控制样式去掉秒年 #changeContab /deep/ #tab-0 { display: none; } #changeContab /deep/ #tab-5 { display: none; } 简易的工具类 可根

    2024年02月11日
    浏览(20)
  • 开源:Taurus.DTS 微服务分布式任务框架,支持即时任务、延时任务、Cron表达式定时任务和广播任务。

    开源:Taurus.DTS 微服务分布式任务框架,支持即时任务、延时任务、Cron表达式定时任务和广播任务。

    在发布完:开源:Taurus.DTC 微服务分布式事务框架,支持 .Net 和 .Net Core 双系列版本,之后想想,好像除了事务外,感觉里面多了一个任务发布订阅的基础功能。 本想既然都有了基础发布订阅功能了,那要不要顺带加上延时发布功能呢?加上了会不会让事务组件不纯了? 经过

    2024年01月18日
    浏览(10)
  • 开源:Taurus.DTS 微服务分布式任务框架,支持即时任务、延时任务、Cron表达式定时任务和广播任务

    开源:Taurus.DTS 微服务分布式任务框架,支持即时任务、延时任务、Cron表达式定时任务和广播任务

    在发布完:开源:Taurus.DTC 微服务分布式事务框架,支持 .Net 和 .Net Core 双系列版本,之后想想,好像除了事务外,感觉里面多了一个任务发布订阅的基础功能。 本想既然都有了基础发布订阅功能了,那要不要顺带加上延时发布功能呢?加上了会不会让事务组件不纯了? 经过

    2024年01月21日
    浏览(10)
  • cron表达式 详解

    cron表达式 详解

    corn表达式是: 由若干数字、空格、符号按一定的规则,组成的一组字符串,从而表达时间的信息。 好像和正则表达式有点类似哈,都是一个字符串表示一些信息。 Cron 表达式生成器: Smart Tools - 智能工具箱 Cron 表达式是一个具有时间含义的字符串,字符串以 5 或 6 个空格隔

    2024年02月04日
    浏览(6)
  • Cron表达式介绍与示例

      1. 概念介绍 Cron表达式是一个具有时间含义的字符串,字符串以5~6个空格隔开,分为6~7个域,格式为 X X X X X X X 。其中 X 是一个域的占位符。最后一个代表年份的域非必须,可省略。单个域有多个取值时,使用半角逗号 , 隔开取值。每个域可以是确定的取值,也可以是具有

    2023年04月17日
    浏览(6)
  • 一篇学会cron表达式

    一篇学会cron表达式

    Cron表达式是一种用于定义定时任务的格式化字符串。它被广泛用于Unix、Linux和类Unix系统中,用于在指定的时间执行预定的任务。Cron表达式由6个字段组成,每个字段通过空格分隔开。 在本文中,我们将学习如何理解和编写Cron表达式。 Cron表达式的格式如下: 每个字段可以使

    2024年02月05日
    浏览(9)
  • cron表达式语法规则及常见示例

    cron表达式最初是由Unix操作系统中的cron守护进程所使用的一种语法规则,用于设置定时任务。cron守护进程是Unix系统中的一个后台进程,用于周期性地执行指定的命令或脚本。它可以根据用户的需求,按照指定的时间间隔或时间点来执行任务,通常用于定时备份、清理日志、

    2024年02月09日
    浏览(13)
  • Linux|CronTab 的 Cron 表达式

    经典 Cron 表达式包含 5 个位置,中间用空格分隔。其标准语法如下: 其中: [minute] 表示分钟,取值范围 [0, 59] [hour] 表示小时,取值范围 [0, 23] [day of month] 表示日期,取值范围 [0, 31] [month] 表示月份,取值范围 [1, 12] ,也可以用名称简写( Jan - Dec ) [day of week] 表示星期,取值

    2024年02月06日
    浏览(8)
  • 深度解析Cron表达式:精确控制任务调度的艺术

    深度解析Cron表达式:精确控制任务调度的艺术

    深度解析Cron表达式:精确控制任务调度的艺术 希望我们都可以满怀期待的路过每一个转角 去遇见 那个属于自己故事的开始 去追寻那个最真实的自己 去放下 去拿起 安然,自得,不受世俗牵绊… 导言 在计算机科学领域,任务调度是一项关键的工作。而Cron表达式就是一种强

    2024年02月02日
    浏览(9)
  • java中cron表达式 每10分钟执行一次

    在Java中,可以使用Quartz框架来定义和调度任务,包括使用Cron表达式来定义任务的执行时间。下面是一个使用Quartz框架实现每10分钟执行一次任务的示例: 添加Quartz依赖 在Maven项目中,添加以下依赖到pom.xml文件中: xml dependency     groupIdorg.quartz-scheduler/groupId     artifactIdquar

    2024年02月13日
    浏览(7)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包