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 定時任務(wù),怎么實現(xiàn)任務(wù)動態(tài)增刪啟停?

        共 1044字,需瀏覽 3分鐘

         ·

        2021-10-24 17:32

        點擊關(guān)注下方公眾號,架構(gòu)師全套資料 都在這里
        0、2T架構(gòu)師學(xué)習(xí)資料干貨分享
        上一篇:深圳一普通中學(xué)老師工資單曝光,秒殺程序員,網(wǎng)友:敢問是哪個學(xué)校畢業(yè)的?

        作者:jessehua
        鏈接:https://www.jianshu.com/p/0f68936393fd

        在spring boot項目中,可以通過@EnableScheduling注解和@Scheduled注解實現(xiàn)定時任務(wù),也可以通過SchedulingConfigurer接口來實現(xiàn)定時任務(wù)。但是這兩種方式不能動態(tài)添加、刪除、啟動、停止任務(wù)。要實現(xiàn)動態(tài)增刪啟停定時任務(wù)功能,比較廣泛的做法是集成Quartz框架。

        但是本人的開發(fā)原則是:在滿足項目需求的情況下,盡量少的依賴其它框架,避免項目過于臃腫和復(fù)雜。查看spring-context這個jar包中org.springframework.scheduling.ScheduledTaskRegistrar這個類的源代碼,發(fā)現(xiàn)可以通過改造這個類就能實現(xiàn)動態(tài)增刪啟停定時任務(wù)功能。

        定時任務(wù)列表頁

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

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

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

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

        public?final?class?ScheduledTask?{

        ????volatile?ScheduledFuture?future;

        ????/**
        ?????*?取消定時任務(wù)
        ?????*/

        ????public?void?cancel()?{
        ????????ScheduledFuture?future?=?this.future;
        ????????if?(future?!=?null)?{
        ????????????future.cancel(true);
        ????????}
        ????}
        }

        添加Runnable接口實現(xiàn)類,被定時任務(wù)線程池調(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("定時任務(wù)開始執(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("定時任務(wù)執(zhí)行異常?- bean:%s,方法:%s,參數(shù):%s ",?beanName,?methodName,?params),?ex);
        ????????}

        ????????long?times?=?System.currentTimeMillis()?-?startTime;
        ????????logger.info("定時任務(wù)執(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);
        ????}
        }

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

        @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();
        ????}
        }

        添加定時任務(wù)示例類

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

        ????public?void?taskNoParams()?{
        ????????System.out.println("執(zhí)行無參示例任務(wù)");
        ????}
        }

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

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

        添加定時任務(wù)實體類

        public class SysJobPO {
        /**
        * 任務(wù)ID
        */
        private Integer jobId;
        /**
        * bean名稱
        */
        private String beanName;
        /**
        * 方法名稱
        */
        private String methodName;
        /**
        * 方法參數(shù)
        */
        private String methodParams;
        /**
        * cron表達(dá)式
        */
        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;
        }

        }

        新增定時任務(wù)

        新增定時任務(wù)

        新增定時任務(wù)

        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();

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

        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();

        刪除定時任務(wù)

        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();

        定時任務(wù)啟動/停止?fàn)顟B(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類,當(dāng)spring boot項目啟動完成后,加載數(shù)據(jù)庫里狀態(tài)為正常的定時任務(wù)。另外,Spring Boot 系列面試題和答案全部整理好了,微信搜索互聯(lián)網(wǎng)架構(gòu)師,在后臺發(fā)送:2T,可以在線閱讀。

        ?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("定時任務(wù)已加載完畢...");????????}????}}工具類SpringContextUtils,用來從spring容器里獲取bean" data-itemshowtype="0" tab="innerlink" data-linktype="2" wah-hotarea="click" hasload="1" style="outline: 0px; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); cursor: pointer; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">@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)為正常的定時任務(wù)
        ????????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("定時任務(wù)已加載完畢...");
        ????????}
        ????}
        }

        工具類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);
        ????}
        }



        瀏覽 33
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        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>
            亚洲男女激情 | 亚洲精品久久久 | 夜夜看av | 可以看的黄色视频 | free×性护士vidos猛烈 | 欧美少妇毛茸茸 | 国产尤物av尤物在线看 | 波多野结衣在线视频免费看 | 麻酥酥视频 | 国产又爽 又黄 免费网站视频 |