欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Springboot實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù)流程詳解

 更新時(shí)間:2022年09月10日 11:41:17   作者:CzRger  
通過重寫SchedulingConfigurer方法實(shí)現(xiàn)對(duì)定時(shí)任務(wù)的操作,單次執(zhí)行、停止、啟動(dòng)三個(gè)主要的基本功能,動(dòng)態(tài)的從數(shù)據(jù)庫(kù)中獲取配置的定時(shí)任務(wù)cron信息,通過反射的方式靈活定位到具體的類與方法中

一、靜態(tài)

靜態(tài)的定時(shí)任務(wù)可以直接使用注解@Scheduled,并在啟動(dòng)類上配置@EnableScheduling即可

  @PostMapping("/list/test1")
  @Async
  @Scheduled(cron = "0 * * * * ?")
  public void test1() throws Exception {
    Object obj = this.getClass().newInstance();
    log.info("執(zhí)行靜態(tài)定時(shí)任務(wù)時(shí)間/test1:param:{}", new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
  }

二、動(dòng)態(tài)

動(dòng)態(tài)的定時(shí)任務(wù)需要根據(jù)ScheduledTaskRegistrar的addTriggerTask方法進(jìn)行實(shí)現(xiàn),網(wǎng)上大多數(shù)資料是在Trigger中動(dòng)態(tài)查詢cron表達(dá)式來實(shí)現(xiàn),但存在一個(gè)問題就是只有在下一次執(zhí)行的時(shí)候才會(huì)刷新,比如一開始設(shè)置的每天12點(diǎn)執(zhí)行一次,如果項(xiàng)目啟動(dòng)后,修改為每小時(shí)執(zhí)行一次的話,需要等到下一次12點(diǎn)執(zhí)行之后,才會(huì)刷新cron表達(dá)式,所以通過對(duì)ScheduledTaskRegistrar的源碼分析,構(gòu)建了以下解決方案,可以實(shí)現(xiàn)對(duì)定時(shí)任務(wù)的額單次額外執(zhí)行、停止、啟動(dòng)三個(gè)基本功能。在此只列出關(guān)鍵代碼,關(guān)于項(xiàng)目啟動(dòng)、數(shù)據(jù)庫(kù)連接的代碼等就不過多說明了。

1、基本代碼

首先新建定時(shí)任務(wù)信息的庫(kù)表及實(shí)體類

@Data
@EqualsAndHashCode(callSuper = false)
@TableName("cron")
public class Cron implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.ASSIGN_UUID)
    private String id;
    private String cronFlag;
    private String cronExpression;
    private String className;
    private String methodName;
}

用于獲取bean實(shí)例的工具類

@Component
public class BeansUtils implements ApplicationContextAware {
  private static ApplicationContext context;
  public static <T> T getBean(Class<T> bean) {
    return context.getBean(bean);
  }
  public static <T> T getBean(String var1, @Nullable Class<T> var2){
    return context.getBean(var1, var2);
  }
  public static ApplicationContext getContext() {
    return context;
  }
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    context = applicationContext;
  }
}

實(shí)現(xiàn)定時(shí)任務(wù)類

@Configuration
@EnableScheduling
@EnableAsync
@Slf4j
public class RCScheduleTask implements SchedulingConfigurer {
  @Autowired
  private ICronService iCronService;
  private static ScheduledTaskRegistrar scheduledTaskRegistrar;
  public static Map<String, TriggerTask> triggerTaskMap;
  @Override
  public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    scheduledTaskRegistrar = taskRegistrar;
    Cron cron = new Cron();
    cron.setCronFlag("1");
    List<Cron> list = iCronService.getList(cron);
    if (list != null) {
      initTriggerTask(list);
    }
  }
  public void initTriggerTask(List<Cron> list) {
    triggerTaskMap = new HashMap<>();
    for (Cron cron : list) {
      TriggerTask triggerTask = new TriggerTask(getRunnable(cron), getTrigger(cron));
      scheduledTaskRegistrar.addTriggerTask(triggerTask);
      triggerTaskMap.put(cron.getId(), triggerTask);
    }
  }
  private static Runnable getRunnable(Cron cron) {
    return new Runnable() {
      @Override
      public void run() {
        Class<?> clazz;
        try {
          clazz = Class.forName(cron.getClassName());
          Object bean = BeansUtils.getBean(clazz);
          Method method = ReflectionUtils.findMethod(bean.getClass(), cron.getMethodName());
          ReflectionUtils.invokeMethod(method, bean);
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    };
  }
  private static Trigger getTrigger(Cron cron) {
    return new Trigger() {
      @Override
      public Date nextExecutionTime(TriggerContext triggerContext) {
        CronTrigger trigger = new CronTrigger(cron.getCronExpression());
        Date nextExec = trigger.nextExecutionTime(triggerContext);
        return nextExec;
      }
    };
  }
  public static boolean run(String id) {
    TriggerTask tt = triggerTaskMap.get(id);
    if (tt != null) {
      tt.getRunnable().run();
      return true;
    }
    return false;
  }
  public static boolean stop(String id) {
    TriggerTask tt = triggerTaskMap.get(id);
    if (tt != null) {
      Set<ScheduledTask> scheduledTasks = scheduledTaskRegistrar.getScheduledTasks();
      for (ScheduledTask st:scheduledTasks) {
        boolean b = st.getTask().getRunnable() == tt.getRunnable();
        if (b) {
          st.cancel();
          return true;
        }
      }
    }
    return false;
  }
  public static boolean start(Cron cron) throws Exception {
    try {
      triggerTaskMap.remove(cron.getId());
      TriggerTask tt = new TriggerTask(getRunnable(cron), getTrigger(cron));
      triggerTaskMap.put(cron.getId(), tt);
      scheduledTaskRegistrar.scheduleTriggerTask(tt);
      return true;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return false;
  }
}

控制調(diào)用類,RCResult為自定義封裝的返回結(jié)果類,可自行定義

@RestController
@RequestMapping("/cron")
@Slf4j
public class CronController {
  @Autowired
  private ICronService iCronService;
  @Autowired
  private RCScheduleTask rcScheduleTask;
  @GetMapping("/task/1")
  @Async
  public void task1() {
    log.info("模擬任務(wù)1-執(zhí)行時(shí)間:{}", new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
  }
  @GetMapping("/task/2")
  @Async
  public void task2() {
    log.info("模擬任務(wù)2-執(zhí)行時(shí)間:{}", new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
  }
  @GetMapping("/task/3")
  @Async
  public void task3() {
    log.info("模擬任務(wù)3-執(zhí)行時(shí)間:{}", new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
  }
  @GetMapping("/run/{id}")
  public RCResult run(@PathVariable("id") String id) {
    Boolean b = RCScheduleTask.run(id);
    if (b) {
      return RCResult.ok("執(zhí)行任務(wù)" + id + "成功");
    }
    return RCResult.error("執(zhí)行任務(wù)" + id + "失敗");
  }
  @GetMapping("/stop/{id}")
  public RCResult stop(@PathVariable("id") String id) {
    Boolean b = RCScheduleTask.stop(id);
    if (b) {
      return RCResult.ok("停止任務(wù)" + id + "成功");
    }
    return RCResult.error("停止任務(wù)" + id + "失敗");
  }
  @GetMapping("/start/{id}")
  public RCResult start(@PathVariable("id") String id) throws Exception {
    Cron cron = iCronService.getById(id);
    if (cron != null) {
      Boolean b = RCScheduleTask.start(cron);
      if (b) {
        return RCResult.ok("開始任務(wù)" + id + "成功");
      }
    }
    return RCResult.error("開始任務(wù)" + id + "失敗");
  }
}

2、方案詳解

2.1 初始化

通過@EnableScheduling和@EnableAsync兩個(gè)標(biāo)簽,開啟定時(shí)任務(wù)和多線程

@Configuration
@EnableScheduling
@EnableAsync
@Slf4j
public class RCScheduleTask implements SchedulingConfigurer {

重寫SchedulingConfigurer的configureTasks方法,可以在項(xiàng)目啟動(dòng)時(shí)自動(dòng)加載狀態(tài)為啟用(cron_flag為1)定時(shí)任務(wù)列表

@Override
  public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    scheduledTaskRegistrar = taskRegistrar;
    Cron cron = new Cron();
    cron.setCronFlag("1");
    List<Cron> list = iCronService.getList(cron);
    if (list != null) {
      initTriggerTask(list);
    }
  }

TriggerTask創(chuàng)建的兩個(gè)參數(shù)Runnable和Trigger可以理解為實(shí)現(xiàn)的操作和執(zhí)行的周期,在Runnable中我們通過反射的方式,從庫(kù)中取到調(diào)用方法的類名和方法名,來執(zhí)行接口操作,在Trigger中我們根據(jù)庫(kù)中的cron表達(dá)式來設(shè)置執(zhí)行周期

public void initTriggerTask(List<Cron> list) {
    triggerTaskMap = new HashMap<>();
    for (Cron cron : list) {
      TriggerTask triggerTask = new TriggerTask(getRunnable(cron), getTrigger(cron));
      scheduledTaskRegistrar.addTriggerTask(triggerTask);
      triggerTaskMap.put(cron.getId(), triggerTask);
    }
  }
  private static Runnable getRunnable(Cron cron) {
    return new Runnable() {
      @Override
      public void run() {
        Class<?> clazz;
        try {
          clazz = Class.forName(cron.getClassName());
          Object bean = BeansUtils.getBean(clazz);
          Method method = ReflectionUtils.findMethod(bean.getClass(), cron.getMethodName());
          ReflectionUtils.invokeMethod(method, bean);
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    };
  }
  private static Trigger getTrigger(Cron cron) {
    return new Trigger() {
      @Override
      public Date nextExecutionTime(TriggerContext triggerContext) {
        CronTrigger trigger = new CronTrigger(cron.getCronExpression());
        Date nextExec = trigger.nextExecutionTime(triggerContext);
        return nextExec;
      }
    };
  }

庫(kù)表數(shù)據(jù)

從執(zhí)行結(jié)果中可以看到狀態(tài)為已啟用的task1和task2執(zhí)行了,而task3并沒有執(zhí)行

2.2 單次執(zhí)行

我們可以通過獲取TriggerTask的Runnable來執(zhí)行run()方法,來單次執(zhí)行定時(shí)任務(wù)

任務(wù)類中定義run方法

public static boolean run(String id) {
    TriggerTask tt = triggerTaskMap.get(id);
    if (tt != null) {
      tt.getRunnable().run();
      return true;
    }
    return false;
  }

接口調(diào)用

@GetMapping("/run/{id}")
  public RCResult run(@PathVariable("id") String id) {
    Boolean b = RCScheduleTask.run(id);
    if (b) {
      return RCResult.ok("執(zhí)行任務(wù)" + id + "成功");
    }
    return RCResult.error("執(zhí)行任務(wù)" + id + "失敗");
  }

模擬接口調(diào)用

從調(diào)用結(jié)果中可以看到,task1在06秒的時(shí)候單獨(dú)執(zhí)行了一次,并且沒有影響后續(xù)執(zhí)行

2.3 停止任務(wù)

停止任務(wù)需要執(zhí)行ScheduledTask類的cancel()方法,由于在初始化時(shí)通過addTriggerTask方法并不會(huì)立刻加入到ScheduledTask列表中,所以需要在調(diào)用時(shí)通過ScheduledTaskRegistrar獲取ScheduledTask列表,然后與TriggerTask的Runnable進(jìn)行比較判斷是否一致

任務(wù)類中定義stop

public static boolean stop(String id) {
    TriggerTask tt = triggerTaskMap.get(id);
    if (tt != null) {
      Set<ScheduledTask> scheduledTasks = scheduledTaskRegistrar.getScheduledTasks();
      for (ScheduledTask st:scheduledTasks) {
        boolean b = st.getTask().getRunnable() == tt.getRunnable();
        if (b) {
          st.cancel();
          return true;
        }
      }
    }
    return false;
  }

調(diào)用

@GetMapping("/stop/{id}")
  public RCResult stop(@PathVariable("id") String id) {
    Boolean b = RCScheduleTask.stop(id);
    if (b) {
      return RCResult.ok("停止任務(wù)" + id + "成功");
    }
    return RCResult.error("停止任務(wù)" + id + "失敗");
  }

在調(diào)用結(jié)果中可以看到,task1不再執(zhí)行了

2.4 啟用任務(wù)

啟用任務(wù)需要通過ScheduledTaskRegistrar的scheduleTriggerTask()方法進(jìn)行調(diào)用,由于源碼中并未提供針對(duì)Runnable或Trigger的單獨(dú)修改方法,所以在這里我們通過新建實(shí)例進(jìn)行替換,在執(zhí)行前先停止任務(wù)

public static boolean start(Cron cron) throws Exception {
    try {
      stop(cron.getId());
      triggerTaskMap.remove(cron.getId());
      TriggerTask tt = new TriggerTask(getRunnable(cron), getTrigger(cron));
      triggerTaskMap.put(cron.getId(), tt);
      scheduledTaskRegistrar.scheduleTriggerTask(tt);
      return true;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return false;
  }

調(diào)用

@GetMapping("/start/{id}")
  public RCResult start(@PathVariable("id") String id) throws Exception {
    Cron cron = iCronService.getById(id);
    if (cron != null) {
      Boolean b = RCScheduleTask.start(cron);
      if (b) {
        return RCResult.ok("開始任務(wù)" + id + "成功");
      }
    }
    return RCResult.error("開始任務(wù)" + id + "失敗");
  }

通過調(diào)用結(jié)果,可以看到在啟用后,task1重新加入到定時(shí)任務(wù)隊(duì)列中了

在修改task1的執(zhí)行周期后再次調(diào)用start方法

從調(diào)用結(jié)果中可以看到,task1的周期已被更改

三、小結(jié)

昨天剛接觸定時(shí)任務(wù),一開始也是在網(wǎng)上搜索資料,后來發(fā)現(xiàn)案例都無(wú)法滿足自身需求,或者只是講解怎么使用,根本無(wú)法映射到實(shí)際業(yè)務(wù)上,所以通過對(duì)源碼的分析,一層層的梳理,模擬了此篇關(guān)于定時(shí)任務(wù)的解決方案,由于時(shí)間原因,在此省略了前端界面的編寫,但大多數(shù)實(shí)際需求無(wú)非也就是通過一個(gè)開關(guān)或按鈕來對(duì)任務(wù)進(jìn)行啟用、停止操作,或者額外需要單次執(zhí)行任務(wù),在此對(duì)動(dòng)態(tài)定時(shí)任務(wù)的方案進(jìn)行記錄,同時(shí)也希望能幫助到遇到同樣問題的同學(xué)們

到此這篇關(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)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring源碼BeanFactoryPostProcessor詳解

    Spring源碼BeanFactoryPostProcessor詳解

    BeanFactoryPostProcessor的執(zhí)行時(shí)機(jī)是在Spring掃描完成后,Bean初始化前,當(dāng)我們實(shí)現(xiàn)BeanFactoryPostProcessor接口,可以在Bean的初始化之前對(duì)Bean進(jìn)行屬性的修改,下面通過本文看下Spring源碼分析-BeanFactoryPostProcessor的實(shí)例代碼,感興趣的朋友一起看看吧
    2021-11-11
  • Idea中導(dǎo)入新模塊無(wú)法被識(shí)別的問題

    Idea中導(dǎo)入新模塊無(wú)法被識(shí)別的問題

    這篇文章主要介紹了Idea中導(dǎo)入新模塊無(wú)法被識(shí)別的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Java多線程局域網(wǎng)聊天室的實(shí)現(xiàn)

    Java多線程局域網(wǎng)聊天室的實(shí)現(xiàn)

    在學(xué)習(xí)了一個(gè)學(xué)期的java以后,搞了一個(gè)多線程的聊天室,熟悉了一下服務(wù)器和客戶機(jī)的操作。感興趣的小伙伴們可以參考一下
    2021-06-06
  • JAVA十大排序算法之堆排序詳解

    JAVA十大排序算法之堆排序詳解

    這篇文章主要介紹了java中的冒泡排序,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考
    2021-08-08
  • Java 測(cè)試URL地址是否能正常連接的代碼

    Java 測(cè)試URL地址是否能正常連接的代碼

    本文給大家分享兩段代碼分別是java測(cè)試URL地址是否能正常連接和Java檢測(cè)URL是否可用或者可打開的代碼,代碼都很簡(jiǎn)單,有需要的朋友可以參考下
    2016-08-08
  • java獲取反射機(jī)制的3種方法總結(jié)

    java獲取反射機(jī)制的3種方法總結(jié)

    這篇文章主要給大家介紹了關(guān)于java獲取反射機(jī)制的3種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Java之Runnable啟動(dòng)線程的使用方式

    Java之Runnable啟動(dòng)線程的使用方式

    這篇文章主要介紹了Java之Runnable啟動(dòng)線程的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • JAVA并發(fā)中VOLATILE關(guān)鍵字的神奇之處詳解

    JAVA并發(fā)中VOLATILE關(guān)鍵字的神奇之處詳解

    這篇文章主要給大家介紹了關(guān)于JAVA并發(fā)中VOLATILE關(guān)鍵字的神奇之處的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • Java實(shí)現(xiàn)在線編輯預(yù)覽office文檔詳解

    Java實(shí)現(xiàn)在線編輯預(yù)覽office文檔詳解

    PageOffice是一款在線的office編輯軟件,幫助Web應(yīng)用系統(tǒng)或Web網(wǎng)站實(shí)現(xiàn)用戶在線編輯Word、Excel、PowerPoint文檔,下面我們就來看看如何使用Java實(shí)現(xiàn)在線預(yù)覽office吧
    2024-01-01
  • SpringBoot項(xiàng)目打jar包與war包的詳細(xì)步驟

    SpringBoot項(xiàng)目打jar包與war包的詳細(xì)步驟

    SpringBoot和我們之前學(xué)習(xí)的web應(yīng)用程序不一樣,其本質(zhì)上是一個(gè) Java應(yīng)用程序,那么又如何部署呢?這篇文章主要給大家介紹了關(guān)于SpringBoot項(xiàng)目打jar包與war包的詳細(xì)步驟,需要的朋友可以參考下
    2023-02-02

最新評(píng)論