1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        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í)行結(jié)果。

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

        添加Runnable接口實現(xiàn)類,被定時任務線程池調(diào)用,用來執(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í)行結(jié)束?- 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技術(shù)江湖】后回復“PDF”即可領取200+頁的《Java工程師面試指南》

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



        瀏覽 48
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            成人综合影视 | 看黄免费在线 | 91杏吧在线观看 | 啊灬好用力灬嗯灬h视频 | 淫荡日逼网 | 伊人伊人伊人 | 久久精品一区二区三区视频 | 亚洲午夜精品久色 | 午夜福利电影在线观看 | 小舞被唐三扒开腿狂动漫 |