前言
前陣子,有動態定時任務的需求,工期緊張且項目中未引入Quartz
。
此時,似乎只剩下Spring Boot提供的定時任務可以考慮了。
調研
Spring Boot中定時任務,一般是使用@Scheduled
注解進行標注,可以設置對應的cron
表達式或者設置運行間隔周期。
樣例代碼如下所示:
@Scheduled(cron="0/5 * * * * ?")
void testPlaceholder1() {
System.out.println("Execute at " + System.currentTimeMillis());
}
不過使用注解標注定時任務,無法滿足定時任務周期定時更新的需求。
解救
查詢資料,發現可以使用繼承SchedulingConfigurer
接口,實現動態定時任務。
@Slf4j
@Configuration
public class ImepStatusSchedulingConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(
()->{
// 業務邏輯
},
triggerContext -> {
// 此處,cron表達式從數據庫查詢得到
String cron = cronMapper.getCron();
// 根據cron表達式,構建CronTrigger,計算下一次的執行時間
CronTrigger trigger = new CronTrigger(cron);
return trigger.nextExecutionTime(triggerContext);
}
);
}
}
由代碼可知,每次運行定時任務時,均會動態計算下一次的運行時間點。
改動
Cron表達式比較復雜(項目由多人維護),而需求里只要求支持間隔固定秒數執行任務。
因此CronTrigger
不再適合,因此需要進行相應改動。改動如下所示:
// 出於篇幅,此處省略無關代碼
triggerContext -> {
// 此處,duration可以從數據庫查詢得到
int duration = 1;
Date date = triggerContext.lastCompletionTime();
if (date != null) {
Date scheduled = triggerContext.lastScheduledExecutionTime();
if (scheduled != null && date.before(scheduled)) {
// Previous task apparently executed too early...
// Let's simply use the last calculated execution time then,
// in order to prevent accidental re-fires in the same second.
date = scheduled;
}
} else {
date = new Date();
}
date.setTime(date.getTime() + duration * 1000);
return date;
}
如此,就實現了動態設置間隔秒數的定時任務。