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

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

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

一、靜態(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á)式來(lái)實(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á)式,所以通過(guò)對(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ù)連接的代碼等就不過(guò)多說(shuō)明了。

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 初始化

通過(guò)@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中我們通過(guò)反射的方式,從庫(kù)中取到調(diào)用方法的類名和方法名,來(lái)執(zhí)行接口操作,在Trigger中我們根據(jù)庫(kù)中的cron表達(dá)式來(lái)設(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í)行

我們可以通過(guò)獲取TriggerTask的Runnable來(lái)執(zhí)行run()方法,來(lái)單次執(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í)通過(guò)addTriggerTask方法并不會(huì)立刻加入到ScheduledTask列表中,所以需要在調(diào)用時(shí)通過(guò)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ù)需要通過(guò)ScheduledTaskRegistrar的scheduleTriggerTask()方法進(jìn)行調(diào)用,由于源碼中并未提供針對(duì)Runnable或Trigger的單獨(dú)修改方法,所以在這里我們通過(guò)新建實(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 + "失敗");
  }

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

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

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

三、小結(jié)

昨天剛接觸定時(shí)任務(wù),一開始也是在網(wǎng)上搜索資料,后來(lái)發(fā)現(xiàn)案例都無(wú)法滿足自身需求,或者只是講解怎么使用,根本無(wú)法映射到實(shí)際業(yè)務(wù)上,所以通過(guò)對(duì)源碼的分析,一層層的梳理,模擬了此篇關(guān)于定時(shí)任務(wù)的解決方案,由于時(shí)間原因,在此省略了前端界面的編寫,但大多數(shù)實(shí)際需求無(wú)非也就是通過(guò)一個(gè)開關(guān)或按鈕來(lái)對(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)文章

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

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

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

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

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

    Struts2源碼分析之ParametersInterceptor攔截器

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

    通過(guò)實(shí)例解析Java class文件編譯加載過(guò)程

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

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

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

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

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

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

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

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

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

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

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

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

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

最新評(píng)論

师宗县| 剑阁县| 本溪市| 茶陵县| 宁津县| 睢宁县| 名山县| 元江| 太保市| 垫江县| 义马市| 贡嘎县| 江城| 泌阳县| 长子县| 安顺市| 马尔康县| 儋州市| 海林市| 泉州市| 榆林市| 临沧市| 惠安县| 土默特左旗| 揭西县| 工布江达县| 凤冈县| 信宜市| 文昌市| 宜春市| 崇礼县| 万全县| 尉氏县| 蕲春县| 呼和浩特市| 安宁市| 黄大仙区| 平远县| 辽宁省| 若羌县| 茂名市|