<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot實(shí)現(xiàn)固定、動(dòng)態(tài)定時(shí)任務(wù) | 三種實(shí)現(xiàn)方式

          共 8736字,需瀏覽 18分鐘

           ·

          2021-10-01 13:17

          背景:??

          最近要用到這個(gè)定時(shí)任務(wù),之前就簡(jiǎn)單使用注解的那種方式,需求一變化,就得重新修改。

          就想到了動(dòng)態(tài)定時(shí)任務(wù),連接數(shù)據(jù)庫(kù)來(lái)動(dòng)態(tài)選擇,這樣確實(shí)解決了問(wèn)題。

          但是仍然有一個(gè)缺陷,就是沒(méi)法設(shè)置任務(wù)的執(zhí)行時(shí)間,無(wú)法做到像 QQ 發(fā)說(shuō)說(shuō)那樣,給 xdm 祝福生日時(shí),設(shè)定說(shuō)說(shuō)為晚上00:00發(fā)布。

          本文就以上三點(diǎn)用自己的思路寫(xiě)了一個(gè)小Demo,希望對(duì)大家有所幫助。

          ?????

          封面來(lái)自于校園一角,秋意漸濃,思念漸深。

          倒計(jì)時(shí)拉倒計(jì)時(shí)拉,xdm,國(guó)慶就要來(lái)了

          前言:

          閱讀完本文:?????

          1. 知曉 SpringBoot 用注解如何實(shí)現(xiàn)定時(shí)任務(wù)

          2. 明白 SpringBoot 如何實(shí)現(xiàn)一個(gè)動(dòng)態(tài)定時(shí)任務(wù) (與數(shù)據(jù)庫(kù)相關(guān)聯(lián)實(shí)現(xiàn))

          3. 理解 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);
          }
          }
          復(fù)制代碼

          第二步:寫(xiě)一個(gè)類,注入到Spring,關(guān)鍵就是 @Scheduled 注解。() 里就是 cron 表達(dá)式,用來(lái)說(shuō)明這個(gè)方法的執(zhí)行周期的。??

          我常常也記不住,通常是在線生成的:Cron 表達(dá)式在線生成

          /**
          * 定時(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í)行一次");
          }
          }
          復(fù)制代碼

          執(zhí)行效果:

          源碼在文末。??

          二、動(dòng)態(tài)定時(shí)任務(wù)

          其實(shí)也非常的簡(jiǎn)單。

          2.1、建數(shù)據(jù)表

          第一步:建個(gè)數(shù)據(jù)庫(kù)表。

          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í)行一次');
          復(fù)制代碼

          2.2、導(dǎo)入依賴,基礎(chǔ)編碼

          第二步:導(dǎo)入數(shù)據(jù)庫(kù)相關(guān)依賴,做到能從數(shù)據(jù)庫(kù)查詢數(shù)據(jù)。大家都會(huì)。???♂?

          第三步:編碼

          實(shí)體類:

          @Data
          @TableName("tb_cron")
          public class Cron {
          private Long id;
          private String cronExpression;
          private String cronDescribe;
          }
          復(fù)制代碼

          mapper層:

          @Repository
          public interface CronMapper extends BaseMapper<Cron> {
          @Select("select cron_expression from tb_cron where id=1")
          String getCron1();
          }
          復(fù)制代碼

          2.3、主要實(shí)現(xiàn)代碼

          第四步:寫(xiě)一個(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ù)庫(kù)中的cron表達(dá)式?jīng)Q定"),
          //2.設(shè)置執(zhí)行周期(Trigger)
          triggerContext -> {
          //2.1 從數(shù)據(jù)庫(kù)獲取執(zhí)行周期
          String cron = cronMapper.getCron1();
          //2.2 合法性校驗(yàn).
          if (cron!=null) {
          // Omitted Code ..
          }
          //2.3 返回執(zhí)行周期(Date)
          return new CronTrigger(cron).nextExecutionTime(triggerContext);
          }
          );
          }
          }
          復(fù)制代碼

          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)景是我前言中說(shuō)的那樣,是一次性的定時(shí)任務(wù)。如:我設(shè)置了我寫(xiě)的這篇文章的發(fā)布時(shí)間為今天下午的兩點(diǎn),執(zhí)行完就刪除沒(méi)有了。一次性的。

          實(shí)現(xiàn)主要依靠于 TaskSchedulerScheduledFuture<?> schedule(Runnable task, Trigger trigger);方法來(lái)實(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;
          }

          /**
          * 查看已開(kāi)啟但還未執(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í)間(如果這是過(guò)去,則任務(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;
          }
          }
          復(fù)制代碼

          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;
          }
          }

          復(fù)制代碼

          3.3、業(yè)務(wù)代碼

          這里需要注意一個(gè)點(diǎn),我給項(xiàng)目中的  LocalDateTime 做了類型轉(zhuǎn)換。這里沒(méi)貼出來(lái)(主要是復(fù)制以前的代碼遺留下來(lái)的,源碼中都有)

          大家簡(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;
          }

          /**
          * 查看已開(kāi)啟但還未執(zhí)行的動(dòng)態(tài)任務(wù)
          * @return
          */

          @GetMapping
          public List<String> getStartingDynamicTask(){
          return dynamicTask.getTaskList();
          }


          /**
          * 開(kāi)啟一個(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()+" 已開(kāi)啟";
          }


          /**
          * 根據(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ù)已停止";
          }

          }
          復(fù)制代碼

          簡(jiǎn)單封裝的一個(gè)實(shí)體類:

          /**
          * @Author: crush
          * @Date: 2021-07-29 15:35
          * version 1.0
          */

          @Data
          @Accessors(chain = true) // 方便鏈?zhǔn)骄帉?xiě) 習(xí)慣所然
          public class Task {
          /**
          * 動(dòng)態(tài)任務(wù)名曾
          */

          private String name;

          /**
          * 設(shè)定動(dòng)態(tài)任務(wù)開(kāi)始時(shí)間
          */

          private LocalDateTime start;
          }
          復(fù)制代碼

          3.4、效果

          ????

          開(kāi)啟一個(gè)動(dòng)態(tài)任務(wù):

          查看開(kāi)啟還未執(zhí)行的動(dòng)態(tài)任務(wù):

          執(zhí)行結(jié)果:

          和我們代碼中是一模一樣的。

          停止任務(wù):

          再去查看就是已經(jīng)停止的拉

          四、自言自語(yǔ)

          源碼:springboot-scheduled

          本文就是簡(jiǎn)單介紹了,具體使用時(shí)還需要根據(jù)具體情況具體分析啦。


          作者:寧在春
          鏈接:https://juejin.cn/post/7013234573823705102
          來(lái)源:稀土掘金
          著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。



          瀏覽 95
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久久一区二区 | 91 黄网站在线观看 | 麻豆精产成人精品 | 91爱爱爱爱 | 日韩亚洲美洲欧洲综三区一品在线 |