javascript
SpringBoot 实现固定、动态定时任务 | 三种实现方式 | 附源代码
背景:📅
最近要用到這個定時任務,之前就簡單使用注解的那種方式,需求一變化,就得重新修改。
就想到了動態定時任務,連接數據庫來動態選擇,這樣確實解決了問題。
但是仍然有一個缺陷,就是沒法設置任務的執行時間,無法做到像 QQ 發說說那樣,給 xdm 祝福生日時,設定說說為晚上00:00發布。
本文就以上三點用自己的思路寫了一個小Demo,希望對大家有所幫助。
👩?💻
封面:來自于校園一角,秋意漸濃,思念漸深。
前言:
閱讀完本文:🐱?👓
一、注解實現定時任務
用注解實現是真的簡單,只要會 cron 表達式就行。🧙?♂?
第一步: 主啟動類上加上 @EnableScheduling 注解
@EnableScheduling @SpringBootApplication public class SpringBootScheduled {public static void main(String[] args) {SpringApplication.run(SpringBootScheduled.class);} }第二步:寫一個類,注入到Spring,關鍵就是 @Scheduled 注解。 () 里就是 cron 表達式,用來說明這個方法的執行周期的。 🛌
我常常也記不住,通常是在線生成的: Cron 表達式在線生成
/*** 定時任務 靜態定時任務** 第一位,表示秒,取值0-59* 第二位,表示分,取值0-59* 第三位,表示小時,取值0-23* 第四位,日期天/日,取值1-31* 第五位,日期月份,取值1-12* 第六位,星期,取值1-7,1表示星期天,2表示星期一* 第七位,年份,可以留空,取值1970-2099* @author crush* @since 1.0.0* @Date: 2021-07-27 21:13*/ @Component public class SchedulingTaskBasic {/*** 每五秒執行一次*/@Scheduled(cron = "*/5 * * * * ?")private void printNowDate() {long nowDateTime = System.currentTimeMillis();System.out.println("固定定時任務執行:--->"+nowDateTime+",此任務為每五秒執行一次");} }執行效果:
源碼在文末。🏍
二、動態定時任務
其實也非常的簡單。
2.1、建數據表
第一步:建個數據庫表。
CREATE TABLE `tb_cron` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '動態定時任務時間表',`cron_expression` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '定時任務表達式',`cron_describe` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述',PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;INSERT INTO `tb_cron` VALUES (1, '0 0/1 * * * ?', '每分鐘執行一次');2.2、導入依賴,基礎編碼
第二步:導入數據庫相關依賴,做到能從數據庫查詢數據。大家都會。🤸?♂?
第三步: 編碼
實體類:
@Data @TableName("tb_cron") public class Cron {private Long id;private String cronExpression;private String cronDescribe; }mapper層:
@Repository public interface CronMapper extends BaseMapper<Cron> {@Select("select cron_expression from tb_cron where id=1")String getCron1(); }2.3、主要實現代碼
第四步:寫一個類 實現 SchedulingConfigurer🍻
實現 void configureTasks(ScheduledTaskRegistrar taskRegistrar); 方法,此方法的作用就是根據給定的 ScheduledTaskRegistrar 注冊 TaskScheduler 和特定的Task實例
@Component public class CompleteScheduleConfig implements SchedulingConfigurer {@Autowired@SuppressWarnings("all")CronMapper cronMapper;/*** 執行定時任務.*/@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {taskRegistrar.addTriggerTask(//1.添加任務內容(Runnable)() -> System.out.println("執行動態定時任務1: " + LocalDateTime.now().toLocalTime()+",此任務執行周期由數據庫中的cron表達式決定"),//2.設置執行周期(Trigger)triggerContext -> {//2.1 從數據庫獲取執行周期String cron = cronMapper.getCron1();//2.2 合法性校驗.if (cron!=null) {// Omitted Code ..}//2.3 返回執行周期(Date)return new CronTrigger(cron).nextExecutionTime(triggerContext);});} }2.4、效果
注意:當你修改了任務執行周期后,生效時間為執行完最近一次任務后。這一點是需要注意的,用生活中的例子理解就是我們取消電話卡的套餐也要下個月生效,含義是一樣的。
源碼同樣在文末。
三、實現設置時間定時任務
通常業務場景是我前言中說的那樣,是一次性的定時任務。如:我設置了我寫的這篇文章的發布時間為今天下午的兩點,執行完就刪除沒有了。一次性的。
實現主要依靠于 TaskScheduler 的ScheduledFuture<?> schedule(Runnable task, Trigger trigger);方法來實現。其本質和動態定時任務的實現是一樣的。
3.1、實現重點
代碼中都含有注解,不多做闡述。
import cn.hutool.core.convert.ConverterRegistry; import com.crush.scheduled.entity.Task; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.stereotype.Component;import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledFuture;/*** @author crush*/ @Component @Slf4j public class DynamicTaskService {/*** 以下兩個都是線程安全的集合類。*/public Map<String, ScheduledFuture<?>> taskMap = new ConcurrentHashMap<>();public List<String> taskList = new CopyOnWriteArrayList<String>();private final ThreadPoolTaskScheduler syncScheduler;public DynamicTaskService(ThreadPoolTaskScheduler syncScheduler) {this.syncScheduler = syncScheduler;}/*** 查看已開啟但還未執行的動態任務* @return*/public List<String> getTaskList() {return taskList;}/*** 添加一個動態任務** @param task* @return*/public boolean add(Task task) {// 此處的邏輯是 ,如果當前已經有這個名字的任務存在,先刪除之前的,再添加現在的。(即重復就覆蓋)if (null != taskMap.get(task.getName())) {stop(task.getName());}// hutool 工具包下的一個轉換類型工具類 好用的很ConverterRegistry converterRegistry = ConverterRegistry.getInstance();Date startTime = converterRegistry.convert(Date.class, task.getStart());// schedule :調度給定的Runnable ,在指定的執行時間調用它。//一旦調度程序關閉或返回的ScheduledFuture被取消,執行將結束。//參數://任務 – 觸發器觸發時執行的 Runnable//startTime – 任務所需的執行時間(如果這是過去,則任務將立即執行,即盡快執行)ScheduledFuture<?> schedule = syncScheduler.schedule(getRunnable(task), startTime);taskMap.put(task.getName(), schedule);taskList.add(task.getName());return true;}/*** 運行任務** @param task* @return*/public Runnable getRunnable(Task task) {return () -> {log.info("---動態定時任務運行---");try {System.out.println("此時時間==>" + LocalDateTime.now());System.out.println("task中設定的時間==>" + task);Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}log.info("---end--------");};}/*** 停止任務** @param name* @return*/public boolean stop(String name) {if (null == taskMap.get(name)) {return false;}ScheduledFuture<?> scheduledFuture = taskMap.get(name);scheduledFuture.cancel(true);taskMap.remove(name);taskList.remove(name);return true;} }3.2、異步線程池的配置
/*** 異步線程池ThreadPoolExecutor 配置類** @Author: crush* @Date: 2021-07-23 14:14*/ @Configuration public class ThreadPoolTaskExecutorConfig {@Beanpublic ThreadPoolTaskScheduler syncScheduler() {ThreadPoolTaskScheduler syncScheduler = new ThreadPoolTaskScheduler();syncScheduler.setPoolSize(5);// 這里給線程設置名字,主要是為了在項目能夠更快速的定位錯誤。syncScheduler.setThreadGroupName("syncTg");syncScheduler.setThreadNamePrefix("syncThread-");syncScheduler.initialize();return syncScheduler;} }3.3、業務代碼
這里需要注意一個點,我給項目中的 LocalDateTime 做了類型轉換。這里沒貼出來(主要是復制以前的代碼遺留下來的,源碼中都有)
大家簡單使用,可以直接用注解 標注在 LocalDateTime 屬性上即可。
package com.crush.scheduled.controller; import com.crush.scheduled.entity.Task; import com.crush.scheduled.service.DynamicTaskService; import org.springframework.web.bind.annotation.*;import java.util.List;/*** @Author: crush* @Date: 2021-07-29 15:26* version 1.0*/ @RestController @RequestMapping("/dynamicTask") public class DynamicTaskController {private final DynamicTaskService dynamicTask;public DynamicTaskController(DynamicTaskService dynamicTask) {this.dynamicTask = dynamicTask;}/*** 查看已開啟但還未執行的動態任務* @return*/@GetMappingpublic List<String> getStartingDynamicTask(){return dynamicTask.getTaskList();}/*** 開啟一個動態任務* @param task* @return*/@PostMapping("/dynamic")public String startDynamicTask(@RequestBody Task task){// 將這個添加到動態定時任務中去dynamicTask.add(task);return "動態任務:"+task.getName()+" 已開啟";}/*** 根據名稱 停止一個動態任務* @param name* @return*/@DeleteMapping("/{name}")public String stopDynamicTask(@PathVariable("name") String name){// 將這個添加到動態定時任務中去if(!dynamicTask.stop(name)){return "停止失敗,任務已在進行中.";}return "任務已停止";}}簡單封裝的一個實體類:
/*** @Author: crush* @Date: 2021-07-29 15:35* version 1.0*/ @Data @Accessors(chain = true) // 方便鏈式編寫 習慣所然 public class Task {/*** 動態任務名曾*/private String name;/*** 設定動態任務開始時間*/private LocalDateTime start; }3.4、效果
💫💨
開啟一個動態任務:
查看開啟還未執行的動態任務:
執行結果:
和我們代碼中是一模一樣的。
停止任務:
再去查看就是已經停止的拉
四、自言自語
源碼:springboot-scheduled
本文就是簡單介紹了,具體使用時還需要根據具體情況具體分析啦。
你好,我是博主寧在春:主頁👨?💻
希望本篇文章能讓你感到有所收獲!!!
祝 我們:待別日相見時,都已有所成。(?′艸`?)
如果有幫助,一鍵三連哦!!!😁
總結
以上是生活随笔為你收集整理的SpringBoot 实现固定、动态定时任务 | 三种实现方式 | 附源代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 今天,我们来详细的聊一聊SpringBo
- 下一篇: 通过简单例子 | 快速理清 UML类图中