springboot使用ThreadPoolTaskScheduler来实现动态修改定时任务的cron

最近有一个需求,客户需要在页面上选择某些信息,然后选择邮件接收人,然后设置每天哪个时间点发送邮件来新增一个定时任务,而且可以同时执行多个定时任务,还要求这些定时任务有启动、停止、修改的按钮。
去网上翻了一些文章,大部分都是触发了定时任务才能修改下次的执行时间,跟我的需求不符,然后借鉴了一些文章内容实现了自己的需求,接下来上代码。
注释都比较清楚,就不多做解释了。

/***
 *  功能描述:定时任务调度类
 */
@Component
public class DynamicTimedTask {
    private static final Logger logger = LoggerFactory.getLogger(DynamicTimedTask.class);

    //创建map存放线程
    public static ConcurrentHashMap<String, ScheduledFuture> map = new ConcurrentHashMap<String, ScheduledFuture>();
    //利用创建好的调度类统一管理
    //接受任务的返回结果
    private ScheduledFuture<?> future;

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    @Autowired
    private ProductMapper productMapper;

    //线程池任务调度类,自定义的ThreadPoolTaskScheduler
    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
        executor.setPoolSize(20);
        return executor;
    }

    /**
     * 系统初始化启动所有定时任务
     */
    @PostConstruct
    public void init(){
        //获取状态为开启的所有定时任务集合
        List<TaskDto> list = productMapper.selectAllCron();
        for (TaskDto taskDto : list) {
            //参数我删除了一些,你可以根据自己的实体类里有什么添加参数
            this.startCron(taskDto.getCron(),String.valueOf(taskDto.getId()));
            System.out.println("定时任务"+String.valueOf(taskDto.getId())+"初始化启动成功");
        }
    }

    /**
     * 启动定时任务
     *
     * @return
     * @param cron
     * @param id
     */
    public boolean startCron( String id,String cron) {
        boolean flag = false;
        //开启任务,获取线程话柄
        future = threadPoolTaskScheduler.schedule(new SendEmail(id), new CronTrigger(cron));
        //将线程话柄存入map集合,键为任务id,方便后续停止指定任务
        map.put(id,future);
        if (future != null) {
            flag = true;
            System.out.println("定时任务"+id+"已添加");
        } else {
            System.out.println("定时任务"+id+"添加失败");
        }
        return flag;
    }

    /**
     * 停止定时任务
     * @param id
     */
    public boolean stopCron(String id) {
        boolean flag = false;
        //根据任务id获取话柄
        ScheduledFuture scheduledFuture = map.get(id);
        if (scheduledFuture != null) {
            //停止任务
            boolean cancel = scheduledFuture.cancel(true);
            if (cancel) {
                flag = true;
                System.out.println("定时任务"+id+"已停止");
            } else {
                System.out.println("定时任务"+id+"停止失败");
            }
        } else {
            flag = true;
        }
        return flag;
    }
}
@Slf4j
@RestController
@RequestMapping("/task")
public class TaskController extends BaseController {

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private DynamicTimedTask dynamicTimedTask;

	//新增定时任务
    @PostMapping("/addJob")
    public ApiResult add(@RequestBody @ApiIgnore Map<String, Object> map) throws Exception {
        TaskDto taskDto = new TaskDto();
        List<String> prdCodes = (List<String>) map.get("prdCodes");
        List<String> recipientsList = (List<String>) map.get("recipientsList");
        String date = (String) map.get("date");
        //格式化时间
        SimpleDateFormat sdf = new SimpleDateFormat( "HH:mm:ss" );
        //时间转化为cron表达式,比如:09:00:00  转化为  0 0 9 * * ?
        String cron = CronUtil.getCron(sdf.parse(date));
        taskDto.setCron(cron);
        taskDto.setPrdcodes(StringUtil.join(prdCodes,","));
        taskDto.setRecipients(StringUtil.join(recipientsList,","));
        //存入定时任务信息,存入数据库
        int row = productMapper.insertJob(taskDto);
        //插入定时任务信息成功则开启定时任务
        if (row>0){
            dynamicTimedTask.startCron(cron, String.valueOf(taskDto.getId()));
        }
        return null;
    }

	//停止定时任务
    @PostMapping("/stopJob")
    public void stop(@RequestBody @ApiIgnore Map<String, Object> map) {
        String id = (String) map.get("id");
        dynamicTimedTask.stopCron(id);
        //根据id修改定时任务状态为停止,数据库表里有一个标识字段,"0"为启动,"1"为关闭
        productMapper.stopTask(id);
    }

	//停止之后重新开启定时任务
    @PostMapping("/startJob")
    public void startJob(@RequestBody @ApiIgnore Map<String, Object> map) {
        String id = (String) map.get("id");
        //根据任务ID查询定时任务
        TaskDto taskDto = productMapper.selectCron(id);
        if (taskDto != null) {
            dynamicTimedTask.startCron(taskDto.getCron(), id);
        }
    }

    //更新定时任务
    @PostMapping("/updateJob")
    public void updateJob(@RequestBody @ApiIgnore Map<String, Object> map) throws ParseException {
        Map<String, Object> params = new HashMap<>();
        String id = (String) map.get("id");
        String date = (String) map.get("date");
        //格式化时间
        SimpleDateFormat sdf = new SimpleDateFormat( "HH:mm:ss" );
        //时间转化为cron表达式,比如:09:00:00  转化为  0 0 9 * * ?
        String cron = CronUtil.getCron(sdf.parse(date));
        params.put("id", id);
        params.put("cron",cron);
        //先停止任务
        dynamicTimedTask.stopCron(id);
        //更新定时任务的cron
        int row = productMapper.updateTask(params);
        //更新成功则重新执行任务
        if (row>0){
            dynamicTimedTask.startCron(cron,id);
        }
    }

    //删除定时任务
    @PostMapping("/deleteJob")
    public void deleteJob(@RequestBody @ApiIgnore Map<String, Object> map) {
        String id = (String) map.get("id");
        dynamicTimedTask.stopCron(id);
        //删除定时任务
        productMapper.deleteTask(id);
    }
}
总的来说,更新cron并实时生效的逻辑就是先把定时任务停止,然后拿到更改后的cron,再重新开启定时任务.
下面是发送邮件的测试类,实现Runnable.
/***
 *  功能描述:发送邮件代码
 */
public class SendEmail implements Runnable {

    private String id;

    public SendEmail(String id) {
        this.id = id;
    }

    @Override
    public void run() {
        System.out.println("发送成功" + id);
    }
}

最后放上cron的工具类,我的需求是每天某个时间点,大家可以根据自己需求更改dateFormat表达式.

/***
 *  功能描述:日期转换cron表达式
 */
public class CronUtil {
    /***
     *  功能描述:日期转换cron表达式
     * @param date
     * @param dateFormat
     * @return
     */
    public static String formatDateByPattern(Date date, String dateFormat){
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        String formatTimeStr = null;
        if (date != null) {
            formatTimeStr = sdf.format(date);
        }
        return formatTimeStr;
    }
    /***
     * @param date  : 时间点
     * @return
     */
    public static String getCron(java.util.Date  date){
        String dateFormat="ss mm HH * * ?";
        return formatDateByPattern(date, dateFormat);
    }
}
经自己测试,实时更新cron是成功的.

如有错误之处,望各位大佬指正.
  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Spring Boot中,我们可以使用数据库配置的cron表达式来完成定时任务。具体步骤如下: 1. 首先,在数据库中创建一个表来存储定时任务的相关信息,例如任务名称、cron表达式、任务类名等。 2. 创建一个定时任务服务类,用于从数据库中读取定时任务信息,并根据cron表达式执行相应的任务。 3. 在Spring Boot的配置文件中配置数据源和定时任务相关的属性,例如扫描定时任务的包路径、定时任务线程池大小等。 4. 在启动类中添加@EnableScheduling注解,开启定时任务的支持。 5. 在定时任务类中添加@Scheduled注解,并指定cron表达式,以及要执行的方法。 下面是一个示例代码,用于演示如何使用数据库配置的cron表达式来完成定时任务: <<引用>> @Configuration @EnableScheduling public class ScheduledConfig { @Autowired private DataSource dataSource; @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(10); scheduler.setThreadNamePrefix("my-scheduled-task-pool-"); scheduler.setAwaitTerminationSeconds(60); scheduler.setWaitForTasksToCompleteOnShutdown(true); return scheduler; } @Bean public ScheduledTaskRegistrar scheduledTaskRegistrar() { ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar(); registrar.setTaskScheduler(taskScheduler()); registrar.setScheduler(taskScheduler()); registrar.setDataSource(dataSource); return registrar; } } @Component public class MyScheduledTask { @Scheduled(cron = "${my.task.cron}") public void doTask() { // do something } } 在上面的代码中,我们使用了@EnableScheduling注解来开启定时任务的支持,使用@Scheduled注解来指定cron表达式和要执行的方法。同时,我们还创建了一个ScheduledConfig类,用于配置定时任务线程池和数据源,以及一个ScheduledTaskRegistrar类,用于从数据库中读取定时任务信息并执行相应的任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

success321

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值