定时任务在业务生产中有着十分重要的作用,Spring Boot中,基于@Scheduled注解可以快速地实现定时任务。,一般定时任务都是按固定的频率来执行,但是因为一些外部因素需要调整频率时,就得重新走一遍编码、打包、部署的流程。本文将与读者一探索如何解决定时器硬编码问题。
定时器中常用Cron表达式来配置定时任务执行时间,Cron是一种描述定时任务调度的字符串,非常灵活,我们本文的核心就是消除Cron表达式硬编码。对Cron表达式感兴趣的朋友,可以阅读之前的文章。
一、常规定时器配置
在了解动态定时器配置之前,我们先一起回顾一下,常规定时器任务配置是怎么实现的。
1、启动定时任务
在Spring Boot中可以通过 @EnableScheduling 注解开启定时任务。一般是在Spring Boot的这一类上添加此注解。
@SpringBootApplication@EnableSchedulingpublic class CustomApplication {public static void main(String[] args) {SpringApplication.run(DuckdbApplication.class, args);}}
2、创建定时任务
创建定时任务是使用 @Scheduled 注解指定执行频率。我们将使用Cron表达式描述任务的调度时间。这里我们配置每20秒执行一次。
@Componentpublic class FixedScheduler {private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");@Scheduled(cron = "0/20 * * * * ?")public void task1() {System.out.println("任务1执行。当前时间:"+ dateTimeFormatter.format(LocalDateTime.now()));}}
项目启动后,执行结果如下:
任务1执行。当前时间:13:38:40任务1执行。当前时间:13:39:00任务1执行。当前时间:13:39:20任务1执行。当前时间:13:39:40任务1执行。当前时间:13:40:00任务1执行。当前时间:13:40:20任务1执行。当前时间:13:40:40....
3、调度分析
上述示例是平时大家创建定时任务使用比较多的方式,这种方式,也是最常用的,但是我们想要更灵活的配置,比较增加一个状态位,根据状态位打开或关闭任务,或是改变执行频率就不能实现了,原因在于Cron表达式是硬编码。不能动态改变。
二、动态定时任务配置
动态定时任务配置与硬编码定时任务不同的地方在于任务的创建,第一步都是通过 @EnableScheduling 注解开启定时任务。下面我们创建一个动态定时任务。
/*** @Author: 黑曜石* @Date: 2024-11-12* @Description: 创建动态定时任务* @VERSION: 0.1*/@Componentpublic class DynamicScheduler {@AutowiredThreadPoolTaskScheduler taskScheduler;private ScheduledFuture<?> scheduledFuture = null;private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");// 当前执行的Cron表达式private String currCron;/*** 设置任务* @param cron cron表达式*/public void setTask(String cron) {this.currCron = cron;pause();System.out.println("==== 设置新任务("+cron+") =====");// 创建定时任务scheduledFuture = taskScheduler.schedule(() -> System.out.println("任务2执行。当前时间:"+ dateTimeFormatter.format(LocalDateTime.now())), new CronTrigger(cron));}/*** 暂停当前任务*/public void pause() {if (scheduledFuture != null) {// 停止之前的任务scheduledFuture.cancel(false);scheduledFuture = null;}}/*** 启动当前任务*/public void start() {if (scheduledFuture == null && StringUtils.hasText(this.currCron)) {setTask(currCron);}}}
上述示例中,核心在于停止之前的任务和动态创建定时任务。
// 停止之前的定时任务scheduledFuture.cancel(false);// 创建新的定时任务scheduledFuture = taskScheduler.schedule(() -> System.out.println("任务2执行。当前时间:"+ dateTimeFormatter.format(LocalDateTime.now())), new CronTrigger(cron));
现在我们在项目启动后,调用 setTask 方法设置默认的定时任务,假设我们设置默认调度为20秒一次("0/20 * * * * ?")。在运行一段时间后我们修改为10秒一次("0/10 * * * * ?")。输出如下:
==== 设置新任务(0/20 * * * * ?) =====任务2执行。当前时间:21:28:00任务2执行。当前时间:21:28:20任务2执行。当前时间:21:28:40任务2执行。当前时间:21:29:00==== 设置新任务(0/10 * * * * ?) =====任务2执行。当前时间:21:29:20任务2执行。当前时间:21:29:30任务2执行。当前时间:21:29:40任务2执行。当前时间:21:29:50......
上述示例仅供参与,在实际生产中,大家可根据实际情况包装自己的类。在上述示例中,任务的实现是使用Lambda表达式,感兴趣的读者,可以参考之前的文章:Java后端:通过Function函数式接口与Lambda表达式简化代码
6174

被折叠的 条评论
为什么被折叠?



