在 Spring 中,創建簡單的定時任務可以使用 @Scheduled 注解,但它有一個缺點,其定時的時間不能動態的改變,而使用基於 SchedulingConfigurer 接口的方式可以做到。
@Scheduled
/*
cron表達式語法:[秒] [分] [小時] [日] [月] [周] [年]
@Scheduled(fixedDelay = 5000) //上一次執行完畢時間點之后5秒再執行
@Scheduled(fixedDelayString = "5000") //上一次執行完畢時間點之后5秒再執行
@Scheduled(fixedRate = 5000) //上一次開始執行時間點之后5秒再執行
@Scheduled(initialDelay=1000, fixedRate=5000) //第一次延遲1秒后執行,之后按fixedRate的規則每5秒執行一次
*/
// 標記配置類
@Configuration
// 開啟定時任務
@EnableScheduling
public class Schedule {
@Scheduled(cron = "0/5 * * * * ?")
private void configureTasks() {
System.err.println("基於注解(@Scheduled)的簡單定時器demo: " + LocalDateTime.now());
}
}
SchedulingConfigurer
SchedulingConfigurer 接口可以實現在 @Configuration 類上,同時不要忘記需要 @EnableScheduling 注解。
該接口的實現方法如下:
public void configureTasks(ScheduledTaskRegistrar taskRegistrar)
其中 ScheduledTaskRegistrar 類的方法有 Trigger 觸發的任務、基於 Cron 表達式的任務、定時任務和延時任務。
以 TriggerTask 為例,示例如下:
@Configuration
@EnableScheduling
public class ConfigurerScheduling implements SchedulingConfigurer{
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(
// 執行定時任務
() ->{
System.out.println("基於接口SchedulingConfigurer的動態定時任務:"
+ LocalDateTime.now()+",線程名稱:"+Thread.currentThread().getName()
+ " 線程id:"+Thread.currentThread().getId());
},
// 設置觸發器
triggerContext -> {
return new CronTrigger("0/5 * * * * ?").nextExecutionTime(triggerContext);
}
);
}
}
Thread
默認的,SchedulingConfigurer 使用的也是單線程的方式,如果需要配置多線程,則需要指定 PoolSize
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(10);
taskScheduler.initialize();
taskRegistrar.setTaskScheduler(taskScheduler);
// taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
taskRegistrar.addTriggerTask(...);
}
SpringBoot Demo
pom 依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
yml 配置文件
#設置定時任務
task:
taskName1: #任務名稱
switch: true #是否開啟定時任務
cron: "0/5 * * * * ?" #任務表達式
taskName2:
switch: true
cron: "0/5 * * * * ?"
SchedulingConfigurer
@Configuration
@EnableScheduling
public abstract class ConfigurerSchedule implements SchedulingConfigurer{
private String cron;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
taskRegistrar.addTriggerTask(
// 執行定時任務
() ->{
processTask();
},
// 設置觸發器
triggerContext -> {
if (StringUtils.isEmpty(cron)) {
cron = getCron();
}
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
);
}
/**
* @brief 任務的處理函數
* 本函數需要由派生類根據業務邏輯來實現
*/
protected abstract void processTask();
/**
* @return String
* @brief 獲取定時任務周期表達式
* 本函數由派生類實現,從配置文件,數據庫等方式獲取參數值
*/
protected abstract String getCron();
}
TaskDemo
@Configuration
public class TaskDemo1 extends ConfigurerSchedule{
@Value(value = "${task.taskName1.switch}")
private Boolean isSwitch;
@Value(value = "${task.taskName1.cron}")
private String cron;
@Override
protected void processTask() {
if (isSwitch){
System.out.println("基於接口SchedulingConfigurer的動態定時任務:"
+ LocalDateTime.now()+",線程名稱:"+Thread.currentThread().getName()
+ " 線程id:"+Thread.currentThread().getId());
}
}
@Override
protected String getCron() {
return cron;
}
}
@Configuration
public class TaskDemo2 extends ConfigurerSchedule{
@Value(value = "${task.taskName2.switch}")
private Boolean isSwitch;
@Value(value = "${task.taskName2.cron}")
private String cron;
@Override
protected void processTask() {
if (isSwitch) {
System.out.println("基於接口SchedulingConfigurer的動態定時任務:"
+ LocalDateTime.now() + ",線程名稱:" + Thread.currentThread().getName()
+ " 線程id:" + Thread.currentThread().getId());
}
}
@Override
protected String getCron() {
return cron;
}
}