SpringBoot實(shí)現(xiàn)固定和動(dòng)態(tài)定時(shí)任務(wù)的三種方法
前言:
閱讀完本文:
- 知曉
SpringBoot
用注解如何實(shí)現(xiàn)定時(shí)任務(wù) - 明白
SpringBoot
如何實(shí)現(xiàn)一個(gè)動(dòng)態(tài)定時(shí)任務(wù) (與數(shù)據(jù)庫相關(guān)聯(lián)實(shí)現(xiàn)) - 理解
SpringBoot
實(shí)現(xiàn)設(shè)置時(shí)間執(zhí)行定時(shí)任務(wù) (使用ThreadPoolTaskScheduler
實(shí)現(xiàn))
一、注解實(shí)現(xiàn)定時(shí)任務(wù)
用注解實(shí)現(xiàn)是真的簡(jiǎn)單,只要會(huì) cron 表達(dá)式就行。
第一步: 主啟動(dòng)類上加上 @EnableScheduling
注解
@EnableScheduling @SpringBootApplication public class SpringBootScheduled { public static void main(String[] args) { SpringApplication.run(SpringBootScheduled.class); } }
第二步:寫一個(gè)類,注入到Spring,關(guān)鍵就是 @Scheduled
注解。 () 里就是 cron 表達(dá)式,用來說明這個(gè)方法的執(zhí)行周期的。
/** * 定時(shí)任務(wù) 靜態(tài)定時(shí)任務(wù) * * 第一位,表示秒,取值0-59 * 第二位,表示分,取值0-59 * 第三位,表示小時(shí),取值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 { /** * 每五秒執(zhí)行一次 */ @Scheduled(cron = "*/5 * * * * ?") private void printNowDate() { long nowDateTime = System.currentTimeMillis(); System.out.println("固定定時(shí)任務(wù)執(zhí)行:--->"+nowDateTime+",此任務(wù)為每五秒執(zhí)行一次"); } }
執(zhí)行效果:
二、動(dòng)態(tài)定時(shí)任務(wù)
其實(shí)也非常的簡(jiǎn)單。
2.1、建數(shù)據(jù)表
第一步:建個(gè)數(shù)據(jù)庫表。
CREATE TABLE `tb_cron` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '動(dòng)態(tài)定時(shí)任務(wù)時(shí)間表', `cron_expression` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '定時(shí)任務(wù)表達(dá)式', `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 * * * ?', '每分鐘執(zhí)行一次');
2.2、導(dǎo)入依賴,基礎(chǔ)編碼
第二步:導(dǎo)入數(shù)據(jù)庫相關(guān)依賴,做到能從數(shù)據(jù)庫查詢數(shù)據(jù)。大家都會(huì)。??♂?
第三步: 編碼
實(shí)體類:
@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、主要實(shí)現(xiàn)代碼
第四步:寫一個(gè)類 實(shí)現(xiàn) SchedulingConfigurer
??
實(shí)現(xiàn) void configureTasks(ScheduledTaskRegistrar taskRegistrar);
方法,此方法的作用就是根據(jù)給定的 ScheduledTaskRegistrar 注冊(cè) TaskScheduler 和特定的Task實(shí)例
@Component public class CompleteScheduleConfig implements SchedulingConfigurer { @Autowired @SuppressWarnings("all") CronMapper cronMapper; /** * 執(zhí)行定時(shí)任務(wù). */ @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.addTriggerTask( //1.添加任務(wù)內(nèi)容(Runnable) () -> System.out.println("執(zhí)行動(dòng)態(tài)定時(shí)任務(wù)1: " + LocalDateTime.now().toLocalTime()+",此任務(wù)執(zhí)行周期由數(shù)據(jù)庫中的cron表達(dá)式?jīng)Q定"), //2.設(shè)置執(zhí)行周期(Trigger) triggerContext -> { //2.1 從數(shù)據(jù)庫獲取執(zhí)行周期 String cron = cronMapper.getCron1(); //2.2 合法性校驗(yàn). if (cron!=null) { // Omitted Code .. } //2.3 返回執(zhí)行周期(Date) return new CronTrigger(cron).nextExecutionTime(triggerContext); } ); } }
2.4、效果
注意:當(dāng)你修改了任務(wù)執(zhí)行周期后,生效時(shí)間為執(zhí)行完最近一次任務(wù)后。這一點(diǎn)是需要注意的,用生活中的例子理解就是我們?nèi)∠娫捒ǖ奶撞鸵惨聜€(gè)月生效,含義是一樣的。
三、實(shí)現(xiàn)設(shè)置時(shí)間定時(shí)任務(wù)
通常業(yè)務(wù)場(chǎng)景是我前言中說的那樣,是一次性的定時(shí)任務(wù)。如:我設(shè)置了我寫的這篇文章的發(fā)布時(shí)間為今天下午的兩點(diǎn),執(zhí)行完就刪除沒有了。一次性的。
實(shí)現(xiàn)主要依靠于 TaskScheduler
的 ScheduledFuture<?> schedule(Runnable task, Trigger trigger);
方法來實(shí)現(xiàn)。其本質(zhì)和動(dòng)態(tài)定時(shí)任務(wù)的實(shí)現(xiàn)是一樣的。
3.1、實(shí)現(xiàn)重點(diǎn)
代碼中都含有注解,不多做闡述。
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 { /** * 以下兩個(gè)都是線程安全的集合類。 */ 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; } /** * 查看已開啟但還未執(zhí)行的動(dòng)態(tài)任務(wù) * @return */ public List<String> getTaskList() { return taskList; } /** * 添加一個(gè)動(dòng)態(tài)任務(wù) * * @param task * @return */ public boolean add(Task task) { // 此處的邏輯是 ,如果當(dāng)前已經(jīng)有這個(gè)名字的任務(wù)存在,先刪除之前的,再添加現(xiàn)在的。(即重復(fù)就覆蓋) if (null != taskMap.get(task.getName())) { stop(task.getName()); } // hutool 工具包下的一個(gè)轉(zhuǎn)換類型工具類 好用的很 ConverterRegistry converterRegistry = ConverterRegistry.getInstance(); Date startTime = converterRegistry.convert(Date.class, task.getStart()); // schedule :調(diào)度給定的Runnable ,在指定的執(zhí)行時(shí)間調(diào)用它。 //一旦調(diào)度程序關(guān)閉或返回的ScheduledFuture被取消,執(zhí)行將結(jié)束。 //參數(shù): //任務(wù) – 觸發(fā)器觸發(fā)時(shí)執(zhí)行的 Runnable //startTime – 任務(wù)所需的執(zhí)行時(shí)間(如果這是過去,則任務(wù)將立即執(zhí)行,即盡快執(zhí)行) ScheduledFuture<?> schedule = syncScheduler.schedule(getRunnable(task), startTime); taskMap.put(task.getName(), schedule); taskList.add(task.getName()); return true; } /** * 運(yùn)行任務(wù) * * @param task * @return */ public Runnable getRunnable(Task task) { return () -> { log.info("---動(dòng)態(tài)定時(shí)任務(wù)運(yùn)行---"); try { System.out.println("此時(shí)時(shí)間==>" + LocalDateTime.now()); System.out.println("task中設(shè)定的時(shí)間==>" + task); Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } log.info("---end--------"); }; } /** * 停止任務(wù) * * @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 { @Bean public ThreadPoolTaskScheduler syncScheduler() { ThreadPoolTaskScheduler syncScheduler = new ThreadPoolTaskScheduler(); syncScheduler.setPoolSize(5); // 這里給線程設(shè)置名字,主要是為了在項(xiàng)目能夠更快速的定位錯(cuò)誤。 syncScheduler.setThreadGroupName("syncTg"); syncScheduler.setThreadNamePrefix("syncThread-"); syncScheduler.initialize(); return syncScheduler; } }
3.3、業(yè)務(wù)代碼
這里需要注意一個(gè)點(diǎn),我給項(xiàng)目中的 LocalDateTime
做了類型轉(zhuǎn)換。這里沒貼出來(主要是復(fù)制以前的代碼遺留下來的,源碼中都有)
大家簡(jiǎn)單使用,可以直接用注解 標(biāo)注在 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; } /** * 查看已開啟但還未執(zhí)行的動(dòng)態(tài)任務(wù) * @return */ @GetMapping public List<String> getStartingDynamicTask(){ return dynamicTask.getTaskList(); } /** * 開啟一個(gè)動(dòng)態(tài)任務(wù) * @param task * @return */ @PostMapping("/dynamic") public String startDynamicTask(@RequestBody Task task){ // 將這個(gè)添加到動(dòng)態(tài)定時(shí)任務(wù)中去 dynamicTask.add(task); return "動(dòng)態(tài)任務(wù):"+task.getName()+" 已開啟"; } /** * 根據(jù)名稱 停止一個(gè)動(dòng)態(tài)任務(wù) * @param name * @return */ @DeleteMapping("/{name}") public String stopDynamicTask(@PathVariable("name") String name){ // 將這個(gè)添加到動(dòng)態(tài)定時(shí)任務(wù)中去 if(!dynamicTask.stop(name)){ return "停止失敗,任務(wù)已在進(jìn)行中."; } return "任務(wù)已停止"; } }
簡(jiǎn)單封裝的一個(gè)實(shí)體類:
/** * @Author: crush * @Date: 2021-07-29 15:35 * version 1.0 */ @Data @Accessors(chain = true) // 方便鏈?zhǔn)骄帉?習(xí)慣所然 public class Task { /** * 動(dòng)態(tài)任務(wù)名曾 */ private String name; /** * 設(shè)定動(dòng)態(tài)任務(wù)開始時(shí)間 */ private LocalDateTime start; }
3.4、效果
開啟一個(gè)動(dòng)態(tài)任務(wù):
查看開啟還未執(zhí)行的動(dòng)態(tài)任務(wù):
執(zhí)行結(jié)果:
和我們代碼中是一模一樣的。
停止任務(wù):
再去查看就是已經(jīng)停止的拉
到此這篇關(guān)于SpringBoot實(shí)現(xiàn)固定和動(dòng)態(tài)定時(shí)任務(wù)的三種方法的文章就介紹到這了,更多相關(guān)SpringBoot 固定和動(dòng)態(tài)定時(shí)任務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot定時(shí)任務(wù)兩種(Spring Schedule 與 Quartz 整合 )實(shí)現(xiàn)方法
- SpringBoot中實(shí)現(xiàn)定時(shí)任務(wù)的4種方式詳解
- SpringBoot中實(shí)現(xiàn)定時(shí)任務(wù)的幾種方式
- SpringBoot2 task scheduler 定時(shí)任務(wù)調(diào)度器四種方式
- SpringBoot下使用定時(shí)任務(wù)的方式全揭秘(6種)
- Springboot實(shí)現(xiàn)定時(shí)任務(wù)的4種方式舉例詳解
- SpringBoot實(shí)現(xiàn)定時(shí)任務(wù)的三種方式小結(jié)
- SpringBoot最新定時(shí)任務(wù)的7種實(shí)現(xiàn)方案
相關(guān)文章
Netty分布式NioSocketChannel注冊(cè)到selector方法解析
這篇文章主要為大家介紹了Netty分布式源碼分析NioSocketChannel注冊(cè)到selector方法的解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03spring?boot整合mongo查詢converter異常排查記錄
這篇文章主要為大家介紹了spring?boot整合mongo查詢時(shí)拋出converter異常的排查解決記錄,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-03-03打開.properties中文顯示unicode編碼問題以及解決
這篇文章主要介紹了打開.properties中文顯示unicode編碼問題以及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(44)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望可以幫到你2021-07-07Java?數(shù)據(jù)結(jié)構(gòu)進(jìn)階二叉樹題集下
二叉樹可以簡(jiǎn)單理解為對(duì)于一個(gè)節(jié)點(diǎn)來說,最多擁有一個(gè)上級(jí)節(jié)點(diǎn),同時(shí)最多具備左右兩個(gè)下級(jí)節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)。本文將帶你通過實(shí)際題目來熟練掌握2022-04-04Java簡(jiǎn)單實(shí)現(xiàn)對(duì)一串?dāng)?shù)字采用相應(yīng)的加密策略后傳輸
下面小編就為大家?guī)硪黄狫ava簡(jiǎn)單實(shí)現(xiàn)對(duì)一串?dāng)?shù)字采用相應(yīng)的加密策略后傳輸。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-09-09spring?boot對(duì)敏感信息進(jìn)行加解密的項(xiàng)目實(shí)現(xiàn)
本文主要介紹了spring?boot對(duì)敏感信息進(jìn)行加解密的項(xiàng)目實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04