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

          Spring Boot實現(xiàn)定時任務的動態(tài)增刪啟停

          共 12019字,需瀏覽 25分鐘

           ·

          2021-10-23 09:27

          作者 |?jessehua

          來源 |?https://www.jianshu.com/p/0f68936393fd

          在spring boot項目中,可以通過@EnableScheduling注解和@Scheduled注解實現(xiàn)定時任務,也可以通過SchedulingConfigurer接口來實現(xiàn)定時任務。但是這兩種方式不能動態(tài)添加、刪除、啟動、停止任務。

          要實現(xiàn)動態(tài)增刪啟停定時任務功能,比較廣泛的做法是集成Quartz框架。但是本人的開發(fā)原則是:在滿足項目需求的情況下,盡量少的依賴其它框架,避免項目過于臃腫和復雜。

          查看spring-context這個jar包中org.springframework.scheduling.ScheduledTaskRegistrar這個類的源代碼,發(fā)現(xiàn)可以通過改造這個類就能實現(xiàn)動態(tài)增刪啟停定時任務功能。

          如果您正在學習Spring Boot,推薦一個連載多年還在繼續(xù)更新的免費教程:http://blog.didispace.com/spring-boot-learning-2x/

          定時任務列表頁

          ?定時任務執(zhí)行日志

          添加執(zhí)行定時任務的線程池配置類

          @Configuration??
          public?class?SchedulingConfig?{??
          ????@Bean??
          ????public?TaskScheduler?taskScheduler()?{??
          ????????ThreadPoolTaskScheduler?taskScheduler?=?new?ThreadPoolTaskScheduler();??
          ????????//?定時任務執(zhí)行線程池核心線程數(shù)??
          ????????taskScheduler.setPoolSize(4);??
          ????????taskScheduler.setRemoveOnCancelPolicy(true);??
          ????????taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");??
          ????????return?taskScheduler;??
          ????}??
          }??

          添加ScheduledFuture的包裝類。ScheduledFuture是ScheduledExecutorService定時任務線程池的執(zhí)行結果。

          public?final?class?ScheduledTask?{??
          ??
          ????volatile?ScheduledFuture?future;??
          ??
          ????/**??
          ?????*?取消定時任務??
          ?????*/
          ??
          ????public?void?cancel()?{??
          ????????ScheduledFuture?future?=?this.future;??
          ????????if?(future?!=?null)?{??
          ????????????future.cancel(true);??
          ????????}??
          ????}??
          }??

          添加Runnable接口實現(xiàn)類,被定時任務線程池調用,用來執(zhí)行指定bean里面的方法。

          public?class?SchedulingRunnable?implements?Runnable?{??
          ??
          ????private?static?final?Logger?logger?=?LoggerFactory.getLogger(SchedulingRunnable.class);??
          ??
          ????private?String?beanName;??
          ??
          ????private?String?methodName;??
          ??
          ????private?String?params;??
          ??
          ????public?SchedulingRunnable(String?beanName,?String?methodName)?{??
          ????????this(beanName,?methodName,?null);??
          ????}??
          ??
          ????public?SchedulingRunnable(String?beanName,?String?methodName,?String?params)?{??
          ????????this.beanName?=?beanName;??
          ????????this.methodName?=?methodName;??
          ????????this.params?=?params;??
          ????}??
          ??
          ????@Override??
          ????public?void?run()?{??
          ????????logger.info("定時任務開始執(zhí)行?- bean:{},方法:{},參數(shù):{}",?beanName,?methodName,?params);??
          ????????long?startTime?=?System.currentTimeMillis();??
          ??
          ????????try?{??
          ????????????Object?target?=?SpringContextUtils.getBean(beanName);??
          ??
          ????????????Method?method?=?null;??
          ????????????if?(StringUtils.isNotEmpty(params))?{??
          ????????????????method?=?target.getClass().getDeclaredMethod(methodName,?String.class);??
          ????????????}?else?{??
          ????????????????method?=?target.getClass().getDeclaredMethod(methodName);??
          ????????????}??
          ??
          ????????????ReflectionUtils.makeAccessible(method);??
          ????????????if?(StringUtils.isNotEmpty(params))?{??
          ????????????????method.invoke(target,?params);??
          ????????????}?else?{??
          ????????????????method.invoke(target);??
          ????????????}??
          ????????}?catch?(Exception?ex)?{??
          ????????????logger.error(String.format("定時任務執(zhí)行異常?- bean:%s,方法:%s,參數(shù):%s ",?beanName,?methodName,?params),?ex);??
          ????????}??
          ??
          ????????long?times?=?System.currentTimeMillis()?-?startTime;??
          ????????logger.info("定時任務執(zhí)行結束?- bean:{},方法:{},參數(shù):{},耗時:{}?毫秒",?beanName,?methodName,?params,?times);??
          ????}??
          ??
          ????@Override??
          ????public?boolean?equals(Object?o)?{??
          ????????if?(this?==?o)?return?true;??
          ????????if?(o?==?null?||?getClass()?!=?o.getClass())?return?false;??
          ????????SchedulingRunnable?that?=?(SchedulingRunnable)?o;??
          ????????if?(params?==?null)?{??
          ????????????return?beanName.equals(that.beanName)?&&??
          ????????????????????methodName.equals(that.methodName)?&&??
          ????????????????????that.params?==?null;??
          ????????}??
          ??
          ????????return?beanName.equals(that.beanName)?&&??
          ????????????????methodName.equals(that.methodName)?&&??
          ????????????????params.equals(that.params);??
          ????}??
          ??
          ????@Override??
          ????public?int?hashCode()?{??
          ????????if?(params?==?null)?{??
          ????????????return?Objects.hash(beanName,?methodName);??
          ????????}??
          ??
          ????????return?Objects.hash(beanName,?methodName,?params);??
          ????}??
          }??

          添加定時任務注冊類,用來增加、刪除定時任務。

          @Component??
          public?class?CronTaskRegistrar?implements?DisposableBean?{??
          ??
          ????private?final?Map?scheduledTasks?=?new?ConcurrentHashMap<>(16);??
          ??
          ????@Autowired??
          ????private?TaskScheduler?taskScheduler;??
          ??
          ????public?TaskScheduler?getScheduler()?{??
          ????????return?this.taskScheduler;??
          ????}??
          ??
          ????public?void?addCronTask(Runnable?task,?String?cronExpression)?{??
          ????????addCronTask(new?CronTask(task,?cronExpression));??
          ????}??
          ??
          ????public?void?addCronTask(CronTask?cronTask)?{??
          ????????if?(cronTask?!=?null)?{??
          ????????????Runnable?task?=?cronTask.getRunnable();??
          ????????????if?(this.scheduledTasks.containsKey(task))?{??
          ????????????????removeCronTask(task);??
          ????????????}??
          ??
          ????????????this.scheduledTasks.put(task,?scheduleCronTask(cronTask));??
          ????????}??
          ????}??
          ??
          ????public?void?removeCronTask(Runnable?task)?{??
          ????????ScheduledTask?scheduledTask?=?this.scheduledTasks.remove(task);??
          ????????if?(scheduledTask?!=?null)??
          ????????????scheduledTask.cancel();??
          ????}??
          ??
          ????public?ScheduledTask?scheduleCronTask(CronTask?cronTask)?{??
          ????????ScheduledTask?scheduledTask?=?new?ScheduledTask();??
          ????????scheduledTask.future?=?this.taskScheduler.schedule(cronTask.getRunnable(),?cronTask.getTrigger());??
          ??
          ????????return?scheduledTask;??
          ????}??
          ??
          ??
          ????@Override??
          ????public?void?destroy()?{??
          ????????for?(ScheduledTask?task?:?this.scheduledTasks.values())?{??
          ????????????task.cancel();??
          ????????}??
          ??
          ????????this.scheduledTasks.clear();??
          ????}??
          }??

          添加定時任務示例類

          @Component("demoTask")??
          public?class?DemoTask?{??
          ????public?void?taskWithParams(String?params)?{??
          ????????System.out.println("執(zhí)行有參示例任務:"?+?params);??
          ????}??
          ??
          ????public?void?taskNoParams()?{??
          ????????System.out.println("執(zhí)行無參示例任務");??
          ????}??
          }??

          定時任務數(shù)據(jù)庫表設計

          ?定時任務數(shù)據(jù)庫表設計

          添加定時任務實體類

          public?class?SysJobPO?{??
          ????/**??
          ?????*?任務ID??
          ?????*/
          ??
          ????private?Integer?jobId;??
          ????/**??
          ?????*?bean名稱??
          ?????*/
          ??
          ????private?String?beanName;??
          ????/**??
          ?????*?方法名稱??
          ?????*/
          ??
          ????private?String?methodName;??
          ????/**??
          ?????*?方法參數(shù)??
          ?????*/
          ??
          ????private?String?methodParams;??
          ????/**??
          ?????*?cron表達式??
          ?????*/
          ??
          ????private?String?cronExpression;??
          ????/**??
          ?????*?狀態(tài)(1正常?0暫停)??
          ?????*/
          ??
          ????private?Integer?jobStatus;??
          ????/**??
          ?????*?備注??
          ?????*/
          ??
          ????private?String?remark;??
          ????/**??
          ?????*?創(chuàng)建時間??
          ?????*/
          ??
          ????private?Date?createTime;??
          ????/**??
          ?????*?更新時間??
          ?????*/
          ??
          ????private?Date?updateTime;??
          ??
          ????public?Integer?getJobId()?{??
          ????????return?jobId;??
          ????}??
          ??
          ????public?void?setJobId(Integer?jobId)?{??
          ????????this.jobId?=?jobId;??
          ????}??
          ??
          ????public?String?getBeanName()?{??
          ????????return?beanName;??
          ????}??
          ??
          ????public?void?setBeanName(String?beanName)?{??
          ????????this.beanName?=?beanName;??
          ????}??
          ??
          ????public?String?getMethodName()?{??
          ????????return?methodName;??
          ????}??
          ??
          ????public?void?setMethodName(String?methodName)?{??
          ????????this.methodName?=?methodName;??
          ????}??
          ??
          ????public?String?getMethodParams()?{??
          ????????return?methodParams;??
          ????}??
          ??
          ????public?void?setMethodParams(String?methodParams)?{??
          ????????this.methodParams?=?methodParams;??
          ????}??
          ??
          ????public?String?getCronExpression()?{??
          ????????return?cronExpression;??
          ????}??
          ??
          ????public?void?setCronExpression(String?cronExpression)?{??
          ????????this.cronExpression?=?cronExpression;??
          ????}??
          ??
          ????public?Integer?getJobStatus()?{??
          ????????return?jobStatus;??
          ????}??
          ??
          ????public?void?setJobStatus(Integer?jobStatus)?{??
          ????????this.jobStatus?=?jobStatus;??
          ????}??
          ??
          ????public?String?getRemark()?{??
          ????????return?remark;??
          ????}??
          ??
          ????public?void?setRemark(String?remark)?{??
          ????????this.remark?=?remark;??
          ????}??
          ??
          ????public?Date?getCreateTime()?{??
          ????????return?createTime;??
          ????}??
          ??
          ????public?void?setCreateTime(Date?createTime)?{??
          ????????this.createTime?=?createTime;??
          ????}??
          ??
          ????public?Date?getUpdateTime()?{??
          ????????return?updateTime;??
          ????}??
          ??
          ????public?void?setUpdateTime(Date?updateTime)?{??
          ????????this.updateTime?=?updateTime;??
          ????}??
          ??
          }??

          新增定時任務

          新增定時任務

          boolean?success?=?sysJobRepository.addSysJob(sysJob);??
          if?(!success)??
          ????return?OperationResUtils.fail("新增失敗");??
          else?{??
          ????if?(sysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal()))?{??
          ????????SchedulingRunnable?task?=?new?SchedulingRunnable(sysJob.getBeanName(),?sysJob.getMethodName(),?sysJob.getMethodParams());??
          ????????cronTaskRegistrar.addCronTask(task,?sysJob.getCronExpression());??
          ????}??
          }??
          ??
          return?OperationResUtils.success();??

          修改定時任務,先移除原來的任務,再啟動新任務

          boolean?success?=?sysJobRepository.editSysJob(sysJob);??
          if?(!success)??
          ????return?OperationResUtils.fail("編輯失敗");??
          else?{??
          ????//先移除再添加??
          ????if?(existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal()))?{??
          ????????SchedulingRunnable?task?=?new?SchedulingRunnable(existedSysJob.getBeanName(),?existedSysJob.getMethodName(),?existedSysJob.getMethodParams());??
          ????????cronTaskRegistrar.removeCronTask(task);??
          ????}??
          ??
          ????if?(sysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal()))?{??
          ????????SchedulingRunnable?task?=?new?SchedulingRunnable(sysJob.getBeanName(),?sysJob.getMethodName(),?sysJob.getMethodParams());??
          ????????cronTaskRegistrar.addCronTask(task,?sysJob.getCronExpression());??
          ????}??
          }??
          ??
          return?OperationResUtils.success();??

          刪除定時任務

          boolean?success?=?sysJobRepository.deleteSysJobById(req.getJobId());??
          if?(!success)??
          ????return?OperationResUtils.fail("刪除失敗");??
          else{??
          ????if?(existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal()))?{??
          ????????SchedulingRunnable?task?=?new?SchedulingRunnable(existedSysJob.getBeanName(),?existedSysJob.getMethodName(),?existedSysJob.getMethodParams());??
          ????????cronTaskRegistrar.removeCronTask(task);??
          ????}??
          }??
          ??
          return?OperationResUtils.success();??

          定時任務啟動/停止狀態(tài)切換

          if?(existedSysJob.getJobStatus().equals(SysJobStatus.NORMAL.ordinal()))?{??
          ????SchedulingRunnable?task?=?new?SchedulingRunnable(existedSysJob.getBeanName(),?existedSysJob.getMethodName(),?existedSysJob.getMethodParams());??
          ????cronTaskRegistrar.addCronTask(task,?existedSysJob.getCronExpression());??
          }?else?{??
          ????SchedulingRunnable?task?=?new?SchedulingRunnable(existedSysJob.getBeanName(),?existedSysJob.getMethodName(),?existedSysJob.getMethodParams());??
          ????cronTaskRegistrar.removeCronTask(task);??
          }??

          添加實現(xiàn)了CommandLineRunner接口的SysJobRunner類,當spring boot項目啟動完成后,加載數(shù)據(jù)庫里狀態(tài)為正常的定時任務。

          另外,如果您正在學習Spring Cloud,推薦一個連載多年還在繼續(xù)更新的免費教程:https://blog.didispace.com/spring-cloud-learning/

          @Service??
          public?class?SysJobRunner?implements?CommandLineRunner?{??
          ??
          ????private?static?final?Logger?logger?=?LoggerFactory.getLogger(SysJobRunner.class);??
          ??
          ????@Autowired??
          ????private?ISysJobRepository?sysJobRepository;??
          ??
          ????@Autowired??
          ????private?CronTaskRegistrar?cronTaskRegistrar;??
          ??
          ????@Override??
          ????public?void?run(String...?args)?{??
          ????????//?初始加載數(shù)據(jù)庫里狀態(tài)為正常的定時任務??
          ????????List?jobList?=?sysJobRepository.getSysJobListByStatus(SysJobStatus.NORMAL.ordinal());??
          ????????if?(CollectionUtils.isNotEmpty(jobList))?{??
          ????????????for?(SysJobPO?job?:?jobList)?{??
          ????????????????SchedulingRunnable?task?=?new?SchedulingRunnable(job.getBeanName(),?job.getMethodName(),?job.getMethodParams());??
          ????????????????cronTaskRegistrar.addCronTask(task,?job.getCronExpression());??
          ????????????}??
          ??
          ????????????logger.info("定時任務已加載完畢...");??
          ????????}??
          ????}??
          }??

          工具類SpringContextUtils,用來從spring容器里獲取bean

          @Component??
          public?class?SpringContextUtils?implements?ApplicationContextAware?{??
          ??
          ????private?static?ApplicationContext?applicationContext;??
          ??
          ????@Override??
          ????public?void?setApplicationContext(ApplicationContext?applicationContext)??
          ????????????throws?BeansException?
          {??
          ????????SpringContextUtils.applicationContext?=?applicationContext;??
          ????}??
          ??
          ????public?static?Object?getBean(String?name)?{??
          ????????return?applicationContext.getBean(name);??
          ????}??
          ??
          ????public?static??T?getBean(Class?requiredType)?{??
          ????????return?applicationContext.getBean(requiredType);??
          ????}??
          ??
          ????public?static??T?getBean(String?name,?Class?requiredType)?{??
          ????????return?applicationContext.getBean(name,?requiredType);??
          ????}??
          ??
          ????public?static?boolean?containsBean(String?name)?{??
          ????????return?applicationContext.containsBean(name);??
          ????}??
          ??
          ????public?static?boolean?isSingleton(String?name)?{??
          ????????return?applicationContext.isSingleton(name);??
          ????}??
          ??
          ????public?static?Class?getType(String?name)?{??
          ????????return?applicationContext.getType(name);??
          ????}??
          }

          本文完,參考本文代碼可成功運行,親測!

          關注公眾號【Java技術江湖】后回復“PDF”即可領取200+頁的《Java工程師面試指南》

          強烈推薦,幾乎涵蓋所有Java工程師必知必會的知識點,不管是復習還是面試,都很實用。



          瀏覽 48
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  成人精品一区二区婷婷 | 麻豆视频免费观看 | 国产小视频一区 | 青娱乐成人在线观看视频 | 中文字幕 国产精品 |