Quartz+SpringBoot实现动态定时添加、暂停、恢复、更新任务

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。

第一步、导包,导入quartz包

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.5.7</version>
        </dependency>

        <!--定时任务quartz-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

我代码中用到了 hutool 的工具类

第二步、数据库建表,使用quartz给我们的sql文件建表

在IDEA中双击 shift 键 搜索table_mysql

 点进去复制sql语句在navicat执行,执行后数据库表如下

第三步、创建配置类,主要是往IOC容器注入Scheduler

import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.annotation.Resource;

/**
 * 定时任务配置类
 */
@Configuration
public class ScheduleConfig {

    @Resource
    private SchedulerFactoryBean schedulerFactoryBean;

    @Bean
    public Scheduler scheduler() {
        return schedulerFactoryBean.getScheduler();
    }

}

第四步、创建Job,这是定时任务工作的内容

import org.springframework.data.redis.core.RedisTemplate;

@DisallowConcurrentExecution //不允许并发执行
public class AmazonDataCaptureJob implements Job {
    private static final Logger log = LoggerFactory.getLogger(AmazonDataCaptureJob.class);
   

    @Resource
    private RedisTemplate redisTemplate; 

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
          // 定时任务需要执行的逻辑

    }

}

 第五步、创建DTO,这个可以自己定义


import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;


@Data
public class ScheduleInfoDto implements Serializable {

    @ApiModelProperty(value = "服务名称")
    private String serveName;

    @ApiModelProperty(value = "任务描述")
    private String descName;

    @ApiModelProperty(value = "任务名称")
    private String jobName;

    @ApiModelProperty(value = "任务组")
    private String jobGroup;

    @ApiModelProperty(value = "任务路径")
    private String jobClassName;

    @ApiModelProperty(value = "调度器名称")
    private String triggerName;

    @ApiModelProperty(value = "调度器组")
    private String triggerGroup;

    @ApiModelProperty(value = "表达式")
    private String cronExpression;

    @ApiModelProperty(value = "附加参数,会传给定时任务接口")
    private String extras;

    /**
     *  状态 :
     NONE,
     NORMAL,
     PAUSED,
     COMPLETE,
     ERROR,
     BLOCKED;
     */
    private String triggerState;

}

第六步、创建接口,主要有添加、修改、暂停、恢复、获取所有任务接口



import com.sifan.erp.dto.ScheduleInfoDto;

import java.util.List;

public interface ScheduleService {

    /**
     * 添加定时任务Job
     *
     */
    void addSchedule(ScheduleInfoDto dto);

    void executeOnce(String jobName, String jobGroup);
    /**
     * 暂停定时任务
     *
     * @param jobName
     * @param jobGroup
     */
    void pauseSchedule(String jobName, String jobGroup);

    /**
     * 恢复定时任务
     *
     * @param jobName
     * @param jobGroup
     */
    void resumeSchedule(String jobName, String jobGroup);

    /**
     * 更新定时任务
     *
     */
    void updateSchedule(ScheduleInfoDto dto);

    /**
     * 删除定时任务
     *
     * @param jobName
     * @param jobGroup
     */
    void deleteSchedule(String jobName, String jobGroup);

    /**
     * 获取所有的定时任务*
     * @return 定时任务列表
     */
    List<ScheduleInfoDto> getAllSchedule();
}

第七步、接口的实现类



import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.sifan.erp.dto.ScheduleInfoDto;
import com.sifan.erp.service.ScheduleService;
import lombok.SneakyThrows;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;


@Service
public class ScheduleServiceImpl implements ScheduleService {

    private static final Logger log = LoggerFactory.getLogger(ScheduleServiceImpl.class);
    @Resource
    private Scheduler scheduler;

    /**
     * 添加定时任务Job
     */
    @SneakyThrows
    @Override
    public void addSchedule(ScheduleInfoDto dto) {
        JobKey jobKey = new JobKey(dto.getJobName(), dto.getJobGroup());
        //检查任务key是否存在
        if (scheduler.checkExists(jobKey)) {
            log.warn("该任务名称及任务组已存在!");
            return ;
        }
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("serveName", dto.getServeName());
        if (ObjectUtil.isNotEmpty(dto.getExtras())) {
            jobDataMap.put("extras", dto.getExtras());
        }
        //如果不传Job的话直接使用默认的Job
        if (StrUtil.isEmpty(dto.getJobClassName())) {
             dto.setJobClassName("com.sifan.erp.common.AmazonDataCaptureJob");
        }
        if (StrUtil.isEmpty(dto.getTriggerName())) {
            dto.setTriggerName("jobTrigger" + IdUtil.fastSimpleUUID());
        }
        if (StrUtil.isEmpty(dto.getTriggerGroup())) {
            dto.setTriggerGroup("jobTriggerGroup" + IdUtil.fastSimpleUUID());
        }
        try {
            JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(dto.getJobClassName()))
                    .withIdentity(dto.getJobName(), dto.getJobGroup())
                    .usingJobData(jobDataMap)
                    .withDescription(dto.getDescName())
                    .storeDurably()
                    .build();
            CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                    .withIdentity(dto.getTriggerName(), dto.getTriggerGroup())
                    .startNow()
                    .withSchedule(CronScheduleBuilder.cronSchedule(dto.getCronExpression()))
                    .build();
            scheduler.scheduleJob(jobDetail, cronTrigger);
            log.info("任务添加成功,任务信息:{}",dto);
        } catch (SchedulerException | ClassNotFoundException e) {
            log.error("任务添加异常异常,{}",e);
        }
    }
    /**
     * 执行一次
     */
    @Override
    public void executeOnce(String jobName, String jobGroup) {
        if (StrUtil.isEmpty(jobName) || StrUtil.isEmpty(jobGroup)) {
            log.warn("该任务名称:{} 任务组:{} 不存在!",jobName,jobGroup);
            return ;
        }
        try {
            //执行任务
            scheduler.triggerJob(JobKey.jobKey(jobName, jobGroup));
            log.info("任务名:{},任务组:{} ,执行成功",jobName,jobGroup);
        } catch (SchedulerException e) {
            log.error("任务名:{},任务组:{},执行异常,{}",jobName,jobGroup,e);
        }
    }

    /**
     * 暂停定时任务
     *
     * @param jobName
     * @param jobGroup
     */
    @Override
    public void pauseSchedule(String jobName, String jobGroup) {
        try {
            scheduler.pauseJob(JobKey.jobKey(jobName, jobGroup));
            log.info("任务名:{},任务组:{} ,暂停成功",jobName,jobGroup);
        } catch (SchedulerException e) {
            log.error("任务名:{},任务组:{},暂停异常,{}",jobName,jobGroup,e);
        }
    }

    /**
     * 恢复定时任务
     *
     * @param jobName
     * @param jobGroup
     */
    @Override
    public void resumeSchedule(String jobName, String jobGroup) {
        try {
            scheduler.resumeJob(JobKey.jobKey(jobName, jobGroup));
            log.info("任务名:{},任务组:{} ,恢复成功",jobName,jobGroup);
        } catch (SchedulerException e) {
            log.error("任务名:{},任务组:{},恢复异常,{}",jobName,jobGroup,e);
        }
    }

    /**
     * 更新定时任务
     */
    @SneakyThrows
    @Override
    public void updateSchedule(ScheduleInfoDto dto) {
        JobKey jobKey = new JobKey(dto.getJobName(), dto.getJobGroup());
        //检查任务key是否存在
        if (scheduler.checkExists(jobKey)) {
            deleteSchedule(dto.getJobName(), dto.getJobGroup());
        }
        //按新的trigger重新设置job执行,重启触发器
        addSchedule(dto);
        log.info("任务更新成功:{}",dto);
    }

    /**
     * 删除定时任务
     *
     * @param jobName
     * @param jobGroup
     */
    @Override
    public void deleteSchedule(String jobName, String jobGroup) {
        try {
            scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup));
            log.info("任务名:{},任务组:{} ,删除成功",jobName,jobGroup);
        } catch (SchedulerException e) {
            log.error("任务名:{},任务组:{},删除异常,{}",jobName,jobGroup,e);
        }
    }

    /**
     * 获取所有定时任务*
     * @return
     */
    @Override
    public List<ScheduleInfoDto> getAllSchedule() {
        List<ScheduleInfoDto> list = new ArrayList<ScheduleInfoDto>();
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = null;
        try{
            jobKeys = scheduler.getJobKeys(matcher);
            jobKeys.forEach(jobKey ->{
                try {
                    List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                    triggers.forEach(trigger ->{
                        ScheduleInfoDto scheduleInfoDto = new ScheduleInfoDto();
                        //获取任务名
                        scheduleInfoDto.setJobName(jobKey.getName());
                        //获取任务组
                        scheduleInfoDto.setJobGroup(jobKey.getGroup());
                        try {
                            //获取任务状态
                            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                            scheduleInfoDto.setTriggerState(triggerState.name());
                            //获取任务表达式
                            if (trigger instanceof CronTrigger) {
                                CronTrigger cronTrigger = (CronTrigger) trigger;
                                String cronExpression = cronTrigger.getCronExpression();
                                scheduleInfoDto.setCronExpression(cronExpression);
                            }
                        } catch (SchedulerException e) {
                            log.error("获取所有定时任务TriggerState出错 {}",e);
                        }
                        list.add(scheduleInfoDto);
                    });
                } catch (SchedulerException e) {
                    log.error("获取所有定时任务triggers出错 {}",e);
                }

            });
        }catch (SchedulerException e){
            log.error("获取所有定时任务jobKeys发生异常 ,{}",e);
        }
        return list;
    }


}

第八步、测试


import com.sifan.erp.dto.ScheduleInfoDto;
import com.sifan.erp.service.ScheduleService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

@SpringBootTest
public class QuartzTest {
    @Resource
    private ScheduleService scheduleService;
    @Test
    public void testAddSchedule(){
        ScheduleInfoDto scheduleInfoDto = new ScheduleInfoDto();
        scheduleInfoDto.setJobGroup("LOL");
        scheduleInfoDto.setJobName("寒冰射手");
        scheduleInfoDto.setJobClassName("com.sifan.erp.common.AmazonDataCaptureJob");
        scheduleInfoDto.setCronExpression("0 30 16 * * ?");
        scheduleService.addSchedule(scheduleInfoDto);
    }
    @Test
    public void testExecuteOnce() throws InterruptedException {
        scheduleService.executeOnce("二郎犬","狗子组");
        TimeUnit.SECONDS.sleep(1000);
    }
    @Test
    public void testUdateSchedule(){
        ScheduleInfoDto scheduleInfoDto = new ScheduleInfoDto();
        scheduleInfoDto.setJobGroup("狗子组");
        scheduleInfoDto.setJobName("哈巴狗");
        scheduleInfoDto.setJobClassName("com.sifan.erp.common.AmazonDataCaptureJob");
        scheduleInfoDto.setCronExpression("0 34 16 * * ?");
        scheduleService.updateSchedule(scheduleInfoDto);
    }
    @Test
    public void testGetAllSchedule(){
        List<ScheduleInfoDto> allSchedule = scheduleService.getAllSchedule();
        System.out.println(allSchedule);
    }

    @Test
    public void testPauseSchedule(){
        scheduleService.pauseSchedule("哈巴狗","狗子组");
    }

    @Test
    public void testResumeSchedule(){
        scheduleService.resumeSchedule("哈巴狗","狗子组");
    }
}


版权声明:本文为qq_43657722原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。