一、背景
在工作中宫屠,有些時候我們有些定時任務的執(zhí)行可能是需要動態(tài)修改的列疗,比如: 生成報表,有些項目配置每天的8點生成浪蹂,有些項目配置每天的10點生成抵栈,像這種動態(tài)的任務執(zhí)行時間告材,在不考慮分布式執(zhí)行的情況下,我們可以
使用 Spring Task
來簡單的實現(xiàn)古劲。
二斥赋、需求和實現(xiàn)思路
1、能夠動態(tài)的添加一個定時任務产艾。
在Spring
中存在一個類ThreadPoolTaskScheduler
,它可以實現(xiàn)根據(jù)一個cron表達式
來調(diào)度一個任務疤剑,并返回一個ScheduledFuture
對象。
2闷堡、能夠取消定時任務的執(zhí)行隘膘。
通過調(diào)用上一步的ScheduledFuture
的cancel
方法,就可以將這個任務取消杠览。
3弯菊、動態(tài)的修改任務執(zhí)行的時間。
- 先取消任務踱阿。
- 然后在重新注冊一個任務管钳。
4、獲取定時任務執(zhí)行的異常
ThreadPoolTaskScheduler類中有一個設置ErrorHandler
的方法扫茅,給自己實現(xiàn)的ErrorHandler即可蹋嵌。
提示:
在
Spring
中我們通過@Scheduled
注解來實現(xiàn)的定時任務,底層也是通過ThreadPoolTaskScheduler
來實現(xiàn)的葫隙≡岳茫可以通過ScheduledAnnotationBeanPostProcessor
類來查看。ThreadPoolTaskScheduler
的默認線程數(shù)是1恋脚,這個需要根據(jù)實際的情況進行修改腺办。
三、代碼實現(xiàn)
此處只給出動態(tài)注冊定時任務和取消的定時任務的代碼糟描。
package com.huan.study.task.jobs.tasks;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* @author huan.fu 2021/7/8 - 下午2:46
*/
@Component
@Slf4j
public class DynamicCronTask implements InitializingBean {
@Autowired
private ThreadPoolTaskScheduler taskScheduler;
private ScheduledFuture<?> scheduledFuture;
@Override
public void afterPropertiesSet() throws Exception {
// 動態(tài)啟動一個定時任務
log.info("注冊一個定時任務:每隔1秒執(zhí)行一次");
scheduledFuture = register("* * * * * ?");
// 取消一個調(diào)度
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(5);
log.info("取消調(diào)度");
scheduledFuture.cancel(false);
log.info("取消結(jié)果:" + scheduledFuture.isCancelled());
log.info("重新注冊一個定時任務:每隔2秒執(zhí)行一次");
register("*/2 * * * * ?");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
private ScheduledFuture<?> register(String cron) {
// 高版本使用 CronExpression怀喉,低版本使用 CronSequenceGenerator
boolean validExpression = CronExpression.isValidExpression(cron);
log.info("cron:[{}]是合法的嗎:[{}]", cron, validExpression);
CronExpression expression = CronExpression.parse(cron);
LocalDateTime nextExecTime = expression.next(LocalDateTime.now());
if (null != nextExecTime) {
log.info("定時任務下次執(zhí)行的時間為:[{}]", nextExecTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
return taskScheduler.schedule(new Runnable() {
@Override
public void run() {
log.info("我執(zhí)行了");
}
}, new CronTrigger(cron));
}
}
四、執(zhí)行結(jié)果
五船响、完整代碼
https://gitee.com/huan1993/spring-cloud-parent/tree/master/springboot/springboot-task