spring定时任务实现动态定时任务(启停,周期修改)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/QiaoRui_/article/details/83110794

使用方法:

            复制下方代码,在业务需要处调用(定时任务状态和执行周期被修改后),一定要在项目启动时后立刻执行一次库中全数据调用此方法,List<Cron> crons   Cron中一定要有业务类的包加类名(com.rails.travel.conf.task.myschedule.MyRunnable),有启停的状态,有执行周期这是必须的参数,根据自己业务可以修改一个定时任务或多个因为是list

描述:

            在我们项目中很多会遇到需要定时任务的地方,但是我们如果利用spring的注解定时任务需要修改时间或者启停任务都要修改项目并重新部署和启动项目,这在很多时候是不能满足我们的需求的,这篇文章主要介绍杂么利用spring的定时任务实现对定时任务的动态启停和执行周期的修改

     之前介绍了

          spring注解方式增加定时定时任务:https://blog.csdn.net/QiaoRui_/article/details/82999655

          spring定时任务利用Redis分布式锁实现集群部署:https://blog.csdn.net/QiaoRui_/article/details/83141060

实现思路:

            1、既然要实现动态我们肯定要有我们动态数据,数据当然是存储到数据库中最合适,先创建一个表,表里的字段主要包含id(表id),classname(当前定时任务类名,很重要),cron(执行周期表达式),classparma(执行定时任务的参数),status(是否开启当前定时任务),最好还要有一个对此任务的描述字段,便于维护。表结构如下

            2、表有了数据有了,我们肯定不能总是操作表,我们需要一个管理页面来做这些事,所以我们根据自己的业务需求做一个对该表的增删改查页面。页面如下

           3、动态数据和操作页面都有了,接下来就是我们杂么让spring的定时任务动态获取到数据而且实时生效。网上有对定时任务单纯动态时间周期的,有单纯对定时任务实现启停的,但是这2中方法都是独立的,修改时间是一个对象,启停又是一个对象,这样就会有一个定时任务多次跑的问题,结合2种方法整合一套可以操作到一个定时任务一个对象。

                具体思路是这样的动态获取库中所有定时任务List<Cron> crons,调用startCron方法,利用scheduledFuture.cancel(true);把list中的所有定时任务都先停止,接着判断list中的定时任务是否有效,有效则利用ScheduledFuture<?> future = threadPoolTaskScheduler.schedule((Runnable) Class.forName(cron.getCronClass()).newInstance(), new CronTrigger(cron.getCron()));开启一个新的定时任务,无效则不做操作,此时已经实现了定时任务的启停和动态周期,接着把启动的定时任务对象保存到map中,key为库中的类名,value为ScheduledFuture对象,方便下次根据key获取到对象进行定时任务的停止。

代码如下


import com.rails.extrs.utils.ToolUtil;
import com.rails.travel.core.constant.COMMON_API_WRAPPER_STATIC_VALUE;
import com.rails.travel.core.domain.Cron;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;

import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ScheduledFuture;


@Configuration
public class ScheduleConfig {

    private static final Logger logger = LoggerFactory.getLogger(ScheduleConfig.class);

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;


    /**
     * 存放所有启动定时任务对象,极其重要
     */
    private HashMap<String, ScheduledFuture<?>> scheduleMap = new HashMap<>();

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        return new ThreadPoolTaskScheduler();
    }


    /**
     * @param crons
     * 动态设置定时任务方法
     * 
     * 此方法是真正的动态实现启停和时间周期的关键,你可以针对自己的业务来调用,你对库中的动态数据修改后来调用此方法,每个Cron对象必须要包含,执行周期(cron.getCron()),启停状态(cron.getCronStatus()),执行的类(cron.getCronClass())
     */
    public void startCron(List<Cron> crons){
        try {
            //遍历所有库中动态数据,根据库中class取出所属的定时任务对象进行关闭,每次都会把之前所有的定时任务都关闭,根据新的状态重新启用一次,达到最新配置
            for (Cron cron : crons){
                ScheduledFuture<?> scheduledFuture = scheduleMap.get(cron.getCronClass());
                //一定判空否则出现空指针异常,ToolUtil为自己写的工具类此处只需要判断对象是否为空即可
                if (ToolUtil.isNotEmpty(scheduledFuture)){
                    scheduledFuture.cancel(true);
                }
            }
            //因为下边存储的是新的定时任务对象,以前的定时任务对象已经都停用了,所以旧的数据没用清除掉,这步可以不处理,因为可以是不可重复要被覆盖
            //scheduleMap.clear();
            //遍历库中数据,之前已经把之前所有的定时任务都停用了,现在判断库中如果是启用的重新启用并读取新的数据,把开启的数据对象保存到定时任务对象中以便下次停用
            for (Cron cron : crons){
                //判断当前定时任务是否有效,COMMON_API_WRAPPER_STATIC_VALUE.VALIDFLAG.TRUE为有效标识
                if (cron.getCronStatus().equals(COMMON_API_WRAPPER_STATIC_VALUE.VALIDFLAG.TRUE)) {
                    //开启一个新的任务,库中存储的是全类名(包名加类名)通过反射成java类,读取新的时间
                    ScheduledFuture<?> future = threadPoolTaskScheduler.schedule((Runnable) Class.forName(cron.getCronClass()).newInstance(), new CronTrigger(cron.getCron()));
                    //这一步非常重要,之前直接停用,只停用掉了最后启动的定时任务,前边启用的都没办法停止,所以把每次的对象存到map中可以根据key停用自己想要停用的
                    scheduleMap.put(cron.getCronClass(),future);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

            接着(Runnable) Class.forName(cron.getCronClass()).newInstance()这是一个解析字符串为类名的一个反射方法,我们需要有这个真正执行我们业务代码的类


import java.util.Date;

public class MyRunnable implements Runnable {

    @Override
    public void run() {
        System.out.print("业务执行了" + new Date());
    }
}

             现在动态启停修改已经实现了,还有一个问题就是现在在什么时候调用此方法,当然是在数据库数据被修改和状态被修改后调用此方法即可,但是我们项目启动就要执行定时任务,所以要有一个项目启动成功后把表中数据查询出来调用此方法的一个步骤(项目加载完执行一次调用数据库所有数据并把数据作为参数传递到方法即可)

猜你喜欢

转载自blog.csdn.net/QiaoRui_/article/details/83110794
今日推荐