使用SpringBoot quartz实现动态修改cron表达式,使新表达式立即生效

背景:如果系统设计的定时任务希望实时修改cron表达式并立即生效时,我们就要考虑使用quartz,因为boot自带的定时任务是不能手动重置定时任务,使定时任务立即生效(目前我没发现方法,如果可以实现,可以在评论区告诉我,多谢了。)。boot自带的定时任务是可以重写触发器的,但是重新生效新的cron必须在上次定义的job运行以后才会触发,就比如我们定义每晚4点触发一次,又改成每小时触发一次。这时不会立即生效,必须要等到凌晨四点触发上次任务以后才会将任务改为每小时触发一次,所以boot自带的不满足立即生效的需求,因此有了以下使用quartz实现,直接上代码。

依赖:


    org.springframework.boot
    spring-boot-starter-quartz

工具类QuartzService.java(这里参考其他wiki的,但是忘了连接,并非原创,我这里加了一下时区。):

package com.XXX.XXX.module.backup.timer;

import java.util.*;

import javax.annotation.PostConstruct;

import lombok.extern.log4j.Log4j2;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.DateBuilder;
import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.QuartzJobBean;

/**
 * 调度service
 *
 * @author dbz
 * @date 2022-9-15
 */
@Configuration
@EnableScheduling
@Log4j2
public class QuartzService {
    @Autowired
    private Scheduler scheduler;

    @PostConstruct
    public void startScheduler() {
        try {
            scheduler.start();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 增加一个job
     *
     * @param jobClass     任务实现类
     * @param jobName      任务名称
     * @param jobGroupName 任务组名
     * @param jobTime      时间表达式 (这是每隔多少秒为一次任务)
     * @param jobTimes     运行的次数 (<0:表示不限次数)
     */
    public void addJob(Class jobClass, String jobName, String jobGroupName, int jobTime,
                       int jobTimes) {
        try {
            // 任务名称和组构成任务key
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)
                    .build();
            // 使用simpleTrigger规则
            Trigger trigger = null;
            if (jobTimes < 0) {
                trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
                        .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withIntervalInSeconds(jobTime))
                        .startNow().build();
            } else {
                trigger = TriggerBuilder
                        .newTrigger().withIdentity(jobName, jobGroupName).withSchedule(SimpleScheduleBuilder
                                .repeatSecondlyForever(1).withIntervalInSeconds(jobTime).withRepeatCount(jobTimes))
                        .startNow().build();
            }
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 增加一个job
     *
     * @param jobClass     任务实现类
     * @param jobName      任务名称
     * @param jobGroupName 任务组名
     * @param jobTime      时间表达式 (如:0/5 * * * * ? )
     */
    public void addJob(Class jobClass, String jobName, String jobGroupName, String jobTime) {
        try {
            // 创建jobDetail实例,绑定Job实现类
            // 指明job的名称,所在组的名称,以及绑定job类
            // 任务名称和组构成任务key
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName)
                    .build();
            // 定义调度触发规则
            // 使用cornTrigger规则
            // 触发器key
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
                    .startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
                    .withSchedule(CronScheduleBuilder.cronSchedule(jobTime).inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))).startNow().build();
            // 把作业和触发器注册到任务调度中
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 修改 一个job的 时间表达式
     *
     * @param jobName
     * @param jobGroupName
     * @param jobTime
     */
    public void updateJob(String jobName, String jobGroupName, String jobTime) {
        try {
            log.warn("Start update job ,job name is {}, jobCron is {}", jobName, jobTime);
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
                    .withSchedule(CronScheduleBuilder.cronSchedule(jobTime).inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))).build();
            // 重启触发器
            scheduler.rescheduleJob(triggerKey, trigger);
            log.warn("finish update job!");
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除任务一个job
     *
     * @param jobName      任务名称
     * @param jobGroupName 任务组名
     */
    public void deleteJob(String jobName, String jobGroupName) {
        try {
            scheduler.deleteJob(new JobKey(jobName, jobGroupName));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 暂停一个job
     *
     * @param jobName jobName
     * @param jobGroupName jobName
     */
    public void pauseJob(String jobName, String jobGroupName) {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.pauseJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 恢复一个job
     *
     * @param jobName jobName
     * @param jobGroupName jobGroupName
     */
    public void resumeJob(String jobName, String jobGroupName) {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.resumeJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 立即执行一个job
     *
     * @param jobName jobName
     * @param jobGroupName jobGroupName
     */
    public void runAJobNow(String jobName, String jobGroupName) {
        try {
            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取所有计划中的任务列表
     *
     * @return List
     */
    public List> queryAllJob() {
        List> jobList = null;
        try {
            GroupMatcher matcher = GroupMatcher.anyJobGroup();
            Set jobKeys = scheduler.getJobKeys(matcher);
            jobList = new ArrayList>();
            for (JobKey jobKey : jobKeys) {
                List triggers = scheduler.getTriggersOfJob(jobKey);
                for (Trigger trigger : triggers) {
                    Map map = new HashMap<>();
                    map.put("jobName", jobKey.getName());
                    map.put("jobGroupName", jobKey.getGroup());
                    map.put("description", "触发器:" + trigger.getKey());
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                    map.put("jobStatus", triggerState.name());
                    if (trigger instanceof CronTrigger) {
                        CronTrigger cronTrigger = (CronTrigger) trigger;
                        String cronExpression = cronTrigger.getCronExpression();
                        map.put("jobTime", cronExpression);
                    }
                    jobList.add(map);
                }
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return jobList;
    }

    /**
     * 获取所有正在运行的job
     *
     * @return List
     */
    public List> queryRunJob() {
        List> jobList = null;
        try {
            List executingJobs = scheduler.getCurrentlyExecutingJobs();
            jobList = new ArrayList>(executingJobs.size());
            for (JobExecutionContext executingJob : executingJobs) {
                Map map = new HashMap();
                JobDetail jobDetail = executingJob.getJobDetail();
                JobKey jobKey = jobDetail.getKey();
                Trigger trigger = executingJob.getTrigger();
                map.put("jobName", jobKey.getName());
                map.put("jobGroupName", jobKey.getGroup());
                map.put("description", "触发器:" + trigger.getKey());
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                map.put("jobStatus", triggerState.name());
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger) trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    map.put("jobTime", cronExpression);
                }
                jobList.add(map);
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return jobList;
    }

}

定时任务逻辑xxxJob.java

package com.XXX.XXX.module.backup.timer;

import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

/**
 * 定时全量备份job
 *
 * @author dbz
 * @date 2022-9-15
 */
@Component
@Log4j2
public class SomThingTimeJob extends QuartzJobBean {


    @SneakyThrows
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        log.warn("start SomThingTimeJob !! ");
        // 这里写你的定时任务业务逻辑
        log.warn("finish SomThingTimeJob !! ");
    }
}

添加定时任务

这里是启动默认添加任务,大部分需求场景应该都是启动就要将加工任务跑起来吧,如果是完全动态管理,这里可以写到rest接口逻辑里,实现MVC级动态。

package com.XXX.XXX.module.backup.timer;

import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * 定时任务调度中心
 *
 * @author dbz
 * @date 2022-9-15
 */
@Component
@Log4j2
public class TimeScheduling implements ServletContextListener {

    @Autowired
    private QuartzService quartzService;


    /**
     *
     * 启动添加定时任务
     * @param sce
     */
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        String cron = "从数据库里查最新的";
       

        // 添加job,这里反射class对应之前创建的job类,ID和组自己定义,重置的时候要用到
        quartzService.addJob(SomThingTimeJob.class, "job1", "job1", cron);
    }
}

重置任务

下面这行代码可以放到自己代码业务的 更新cron配置接口中,一旦方法调用完成定时任务会马上生效。

 quartzService.updateJob("job1", "job1", "新的cron表达式");

你可能感兴趣的:(spring,boot,后端,java)