最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

一、靜態(tài)

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

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

二、動態(tài)

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

1、基本代碼

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

@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實例的工具類

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

實現(xiàn)定時任務(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í)行時間:{}", new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
  }
  @GetMapping("/task/2")
  @Async
  public void task2() {
    log.info("模擬任務(wù)2-執(zhí)行時間:{}", new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
  }
  @GetMapping("/task/3")
  @Async
  public void task3() {
    log.info("模擬任務(wù)3-執(zhí)行時間:{}", 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兩個標簽,開啟定時任務(wù)和多線程

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

重寫SchedulingConfigurer的configureTasks方法,可以在項目啟動時自動加載狀態(tài)為啟用(cron_flag為1)定時任務(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)建的兩個參數(shù)Runnable和Trigger可以理解為實現(xiàn)的操作和執(zhí)行的周期,在Runnable中我們通過反射的方式,從庫中取到調(diào)用方法的類名和方法名,來執(zhí)行接口操作,在Trigger中我們根據(jù)庫中的cron表達式來設(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;
      }
    };
  }

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

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

2.2 單次執(zhí)行

我們可以通過獲取TriggerTask的Runnable來執(zhí)行run()方法,來單次執(zhí)行定時任務(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秒的時候單獨執(zhí)行了一次,并且沒有影響后續(xù)執(zhí)行

2.3 停止任務(wù)

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

任務(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()方法進行調(diào)用,由于源碼中并未提供針對Runnable或Trigger的單獨修改方法,所以在這里我們通過新建實例進行替換,在執(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重新加入到定時任務(wù)隊列中了

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

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

三、小結(jié)

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

到此這篇關(guān)于Springboot實現(xiàn)動態(tài)定時任務(wù)流程詳解的文章就介紹到這了,更多相關(guān)Springboot動態(tài)定時任務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot添加自定義攔截器的實現(xiàn)代碼

    SpringBoot添加自定義攔截器的實現(xiàn)代碼

    這篇文章主要介紹了SpringBoot添加自定義攔截器的實現(xiàn)代碼,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-09-09
  • java設(shè)計模式-組合模式詳解

    java設(shè)計模式-組合模式詳解

    這篇文章主要介紹了JAVA設(shè)計模式之組合模式,簡單說明了組合模式的原理,并結(jié)合實例分析了java組合模式的具體用法,需要的朋友可以參考下
    2021-07-07
  • Struts2源碼分析之ParametersInterceptor攔截器

    Struts2源碼分析之ParametersInterceptor攔截器

    這篇文章主要介紹了Struts2源碼分析之ParametersInterceptor攔截器,ParametersInterceptor攔截器其主要功能是把ActionContext中的請求參數(shù)設(shè)置到ValueStack中,,需要的朋友可以參考下
    2019-06-06
  • 通過實例解析Java class文件編譯加載過程

    通過實例解析Java class文件編譯加載過程

    這篇文章主要介紹了通過實例解析Java class文件編譯加載過程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-07-07
  • Java設(shè)計模式之java原型模式詳解

    Java設(shè)計模式之java原型模式詳解

    這篇文章主要介紹了Java設(shè)計模式之原型模式詳解,文中有非常詳細的代碼示例,對正在學習java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-09-09
  • SpringBoot整合WebSocket實現(xiàn)聊天室流程全解

    SpringBoot整合WebSocket實現(xiàn)聊天室流程全解

    WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議。本文將通過SpringBoot集成WebSocket實現(xiàn)簡易聊天室,對大家的學習或者工作具有一定的參考學習價值,感興趣的可以了解一下
    2023-01-01
  • 基于Java內(nèi)存溢出的解決方法詳解

    基于Java內(nèi)存溢出的解決方法詳解

    本篇文章是對Java內(nèi)存溢出的解決方法進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • 優(yōu)化常見的java排序算法

    優(yōu)化常見的java排序算法

    這篇文章主要介紹了Java編程中快速排序算法的實現(xiàn)及相關(guān)算法優(yōu)化,快速排序算法的最差時間復(fù)雜度為(n^2),最優(yōu)時間復(fù)雜度為(n\log n),存在優(yōu)化的空間,需要的朋友可以參考下
    2021-07-07
  • spring的UnexpectedRollbackException事務(wù)嵌套示例解析

    spring的UnexpectedRollbackException事務(wù)嵌套示例解析

    這篇文章主要為大家介紹了spring的UnexpectedRollbackException事務(wù)嵌套示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • 關(guān)于SpringBoot中controller參數(shù)校驗的使用

    關(guān)于SpringBoot中controller參數(shù)校驗的使用

    本文主要介紹了關(guān)于SpringBoot中controller參數(shù)校驗的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-01-01

最新評論

利辛县| 安图县| 万宁市| 涞源县| 衡水市| 永定县| 青州市| 晴隆县| 连南| 华容县| 墨玉县| 双城市| 苗栗市| 伊吾县| 江津市| 阳山县| 武山县| 城市| 蓝田县| 应城市| 江陵县| 青海省| 宁阳县| 资溪县| 东乡族自治县| 龙口市| 读书| 成都市| 任丘市| 南川市| 元江| 文山县| 黄骅市| 万载县| 宁远县| 辽宁省| 武宁县| 淮滨县| 中卫市| 武威市| 都江堰市|