前言
- 傳統(tǒng)定時器是硬編碼。但是有的時候業(yè)務(wù)上需要不斷地調(diào)整
問題描述
- 我們開發(fā)了一個定鬧鐘的功能。這個功能肯定是定時器開發(fā)。但是這就存在一個問題這個定時是動態(tài)的。那么我們?nèi)绾螌崿F(xiàn)呢?請接著看
簡介
- 定時器在開發(fā)中真的算是一種福利了。通過定時器我們省去了很多人力。我們通過定時器將一些繁瑣定期的事情通過代碼去完成。在JAVA開發(fā)中我們通過Timer類可以簡單實現(xiàn)定時器功能。既然是springboot課程今天我們就來看看srpingboot整合定時器的事情
傳統(tǒng)定時器
- 這里使用的是之前課程一的配置。springboot打算是系列講解。所以配置都是承前啟后的。建議大家按順序觀看。
@Component
public class SimpleSchedule {
@Autowired
TestMApper testMapper;
@Scheduled(cron = "*/6 * * * * ?")
private void process() {
List<test> tests = testMapper.getTests();
System.out.println(tests);
}
}
- 定時器的編寫也很簡單,只需要在類或者方法上加上@Scheduled注解。然后配置cron表達式就可以了。這里得注意一下需要在spirngboot啟動類上加上開發(fā)定時器的注解。
@SpringBootApplication
public class CrontabApplication {
public static void main(String[] args) {
SpringApplication.run(CrontabApplication.class, args);
}
}
- 代碼中我們使用的是最簡單的一種方式。
- cron表達式:指定任務(wù)在特定時間執(zhí)行
- fixedDelay:表示上一次任務(wù)執(zhí)行完成后多久再執(zhí)行,參數(shù)類型long,單位:ms
- fixedDelayString:與fixedDelay一樣,只是參數(shù)類型是String
- fixedRate:表示按一定的頻率執(zhí)行任務(wù),參數(shù)類型long,單位:ms 如: fixedRate(5000),表示這個定時器任務(wù)每5秒執(zhí)行一次
- fixedRateString:與fixedRate一樣,只是參數(shù)類型變?yōu)镾tring
- initialDelay:表示延遲多久再第一次執(zhí)行任務(wù),參數(shù)類型為long ,單位:ms
- initialDelayString:與initialDelay一樣,只是參數(shù)類型String
動態(tài)定時器
- 上面的定時器已經(jīng)成功的配置了。但是現(xiàn)在有一個需求客戶想通過頁面定制配置定時器執(zhí)行的頻率。上面代碼我們是寫死6S執(zhí)行一次。如果客戶想通過可視化配置。配置完成之后我總不能再手動改寫代碼吧。那么動態(tài)定時器就產(chǎn)生了。
V1.0
- 既然動態(tài)我們就得將客戶配置的數(shù)據(jù)進行本地化。當然是存儲在數(shù)據(jù)庫中。
- 對應(yīng)的我們新建Mapper查詢定時任務(wù)信息。因為這里只配置了表達式。沒有配置表達式對應(yīng)的定時器。也是為了測試。這里默認表達式就是一個。
@Configuration
public class ScheduleConfigV1 implements SchedulingConfigurer {
@Autowired
CronMapper cronMapper;
@Autowired
TestMapper testMapper;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.addTriggerTask(()-> {
System.out.println("執(zhí)行定時器任務(wù):" + LocalDateTime.now().toLocalTime());
List<test> tests = testMapper.getTests();
System.out.println(tests);
},
triggerContext -> {
List<cron> crons = cronMapper.getCron();
Cron cron = crons.get(0);
return new CronTrigger(cron.getCron()).nextExecutionTime(triggerContext);
});
}
}
- 執(zhí)行這個代碼我們最好先關(guān)掉前面那個靜態(tài)的定時器。這樣看的效果明顯點。首先數(shù)據(jù)庫配置的是6秒執(zhí)行一次。然后把數(shù)據(jù)改成2秒執(zhí)行一次。看看效果。
- 我們發(fā)現(xiàn)只要數(shù)據(jù)庫信息修改了。定時任務(wù)會自動修改頻率的。最重要的是不需要重啟我們的代碼。
- 上面雖然是動態(tài)配置了。但是有一個缺點。就是修改之后生效是在下一次觸發(fā)定時器執(zhí)行后有效。說白了就是一開始一小時執(zhí)行一次,在這期間修改了不能立馬生效必須得到下一次一小時才會去刷新配置。這里的動態(tài)可以理解成懶動態(tài)。
V2.0
- 上面的功能雖然是動態(tài)的。但是對于量產(chǎn)的話肯定是不科學的。首先數(shù)據(jù)庫不可能只存一條數(shù)據(jù)的。
- 如果存多條數(shù)據(jù)那么多條定時規(guī)則與具體的定時器怎么進行匹配呢?
- 既然是動態(tài)的那么如何通過數(shù)據(jù)庫控制定時器的開關(guān)呢?
- 定時任務(wù)的通過代碼啟動實際是scheduler.schedule(task, new CronTrigger("*/2 * * * * ?"));實現(xiàn)的。這個方法返回的對象是ScheduledFuture。通過canel方法取消定時任務(wù)。基于這兩個方法我們來改進下我們之前的定時任務(wù)。
Registar
- 首先我們提供一個注冊器,注冊器的功能就是管理定時任務(wù)。提供增加刪除功能。在增加定時器的節(jié)點上我們調(diào)用scheduler.schedule(task, new CronTrigger("*/2 * * * * ?"));來啟動定時任務(wù)。在刪除節(jié)點上調(diào)用之前獲取的ScheduledFuture來canel這個定時任務(wù)。這樣做的好處我們可以隨時控制定時任務(wù)的開關(guān)
public void addCronTask(Runnable task, String cron) {
addCronTask(new CronTask(task,cron));
}
- 上面添加需要有一個runnable和cron表達式。用一個ConcurrentHashMap來管理添加進來的runnable。runnable為key,ScheduledTask為值。
public ScheduledTask scheduleCronTask(CronTask cronTask) {
ScheduledTask scheduledTask;
scheduledTask = new ScheduledTask();
scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());
return scheduledTask;
}
- 這樣構(gòu)建一個ScheduledTask對象。
public final class ScheduledTask {
public volatile ScheduledFuture<!--?--> future;
/**
* 取消定時任務(wù)
*/
public void cancel() {
ScheduledFuture<!--?--> future = this.future;
if (future != null) {
future.cancel(true);
}
}
}
- 這樣我們就可以通過構(gòu)建一個runnable線程,結(jié)合表達式通過注冊器注冊就可以開啟這個線程已固定頻率執(zhí)行。通過remove關(guān)閉線程。
SchedulingRunnable task = new SchedulingRunnable(TestMapper.class, "getTests", null);
cronTaskRegistrar.addCronTask(task, "0/10 * * * * ?");
- 這樣做的好處是我們可以在表數(shù)據(jù)修改的情況下立馬更新定時任務(wù)規(guī)則。
總結(jié)
-上面的代碼已經(jīng)上傳至gitee
點我傳送
https://gitee.com/zxhTom/crontab.git
- 下面Java類是我們這次使用用到的類。
- SchedulingConfigurer
- DisposableBean
- ConcurrentHashMap
原文鏈接:
https://www.cnblogs.com/zhangxinhua/p/14830103.html
如果覺得本文對你有幫助,可以轉(zhuǎn)發(fā)關(guān)注支持一下






