SpringBoot整合quartz完成定时任务执行配置热修改

1. 依赖


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

2. 逻辑说明

  • 在容器(TaskContext)中每隔N秒扫描数据库中的任务配置
  • 根据配置生成任务。任务不存在则创建;存在则更新

    主要的文件如下:

文件名 功能

TaskConfig

生成scheduler,并将其注入到spring容器中

MyJobFactory

TaskConfig所依赖

QuartzManager

封装了定时任务的操作

TaskContext

任务容器,主要是定时扫描任务配置,并生成/更新任务

TaskMapper

配置查询(Mybatis)

3. 源码

3.1 MyJobFactory

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;

/**
 * @author pp_lan
 * @date 2024/1/2
 */
@Component
public class MyJobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        Object jobInstance = super.createJobInstance(bundle);

        // 注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

3.2 TaskConfig

import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

/**
 * @author pp_lan
 * @date 2024/1/2
 */
@Configuration
@DependsOn("myJobFactory")
public class TaskConfig {

    @Autowired
    private MyJobFactory myJobFactory;

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        schedulerFactoryBean.setJobFactory(myJobFactory);
        return schedulerFactoryBean;
    }

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

3.3 QuartzManager

import com.alibaba.druid.util.StringUtils;
import org.quartz.*;
import org.quartz.impl.calendar.CronCalendar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author pp_lan
 * @date 2024/1/2
 */
@Configuration
public class QuartzManager {

    private static final Logger LOGGER = LoggerFactory.getLogger(QuartzManager.class);

    @Autowired
    private Scheduler scheduler;

    @Autowired
    private List taskList;

    private Map taskMap;

    @PostConstruct
    public void init() throws ParseException, SchedulerException {
        this.taskMap = taskList.stream().collect(Collectors.toMap(t -> t.getClass().getSimpleName(), t -> t));

        // 此处配置不执行任务的时间段
        CronCalendar cronCalendar = new CronCalendar("0 0/1 * * * ?");
        this.scheduler.addCalendar("minute", cronCalendar, false, false);
    }

    public void startJobTask(String name, String group, String cron) throws SchedulerException {
        JobKey jobKey = new JobKey(name, group);
        if (this.scheduler.checkExists(jobKey)) {
            // 任务修改
            modifyJob(name, group, cron);
        } else {
            // 任务新增
            if (taskMap.containsKey(name)) {

                // 执行任务
                Class taskClazz = taskMap.get(name).getClass();
                JobDetail jobDetail = JobBuilder.newJob(taskClazz).withIdentity(name, group).build();

                // 执行时间正则
                CronScheduleBuilder cronBuilder = CronScheduleBuilder.cronSchedule(cron);
                CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(name, group)
                        .withSchedule(cronBuilder)
                        // 排除掉的执行时间
                        .modifiedByCalendar("minute")
                        .build();

                scheduler.scheduleJob(jobDetail, cronTrigger);
            } else {
                LOGGER.debug("任务没有配置执行配置{}", name);
            }
        }
    }

    public String getJobInfo(String name, String group) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey(name, group);
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        return new StringBuilder("time:").append(trigger.getCronExpression()).append("\t state:")
                .append(scheduler.getTriggerState(triggerKey).name()).toString();
    }

    /**
     * 修改
     *
     * @param name
     * @param group
     * @param cron
     * @return
     * @throws SchedulerException
     */
    public boolean modifyJob(String name, String group, String cron) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey(name, group);

        Trigger trigger = scheduler.getTrigger(triggerKey);
        if (trigger == null) {
            LOGGER.info("未存在的触发器[{}-{}]", name, group);
            return false;
        }
        String oldCron = ((CronTrigger) trigger).getCronExpression();
        if (!StringUtils.equals(cron, oldCron)) {
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            CronTrigger newTrigger = TriggerBuilder.newTrigger().withIdentity(name, group).withSchedule(cronScheduleBuilder)
                    .build();

            Date date = scheduler.rescheduleJob(triggerKey, newTrigger);
            return date != null;
        }

        return false;
    }


    public void pauseAll() throws SchedulerException {
        this.scheduler.pauseAll();
    }

    public void pause(String name, String group) throws SchedulerException {
        JobKey jobKey = new JobKey(name, group);
        JobDetail jobDetail = this.scheduler.getJobDetail(jobKey);
        if (jobDetail == null) {
            return;
        }

        this.scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复
     *
     * @throws SchedulerException
     */
    public void resumeAllJob()throws SchedulerException{
        scheduler.resumeAll();
    }

    /**
     * 删除任务
     *
     * @param name
     * @param group
     * @throws SchedulerException
     */
    public void delete(String name, String group) throws SchedulerException {
        JobKey jobKey = new JobKey(name, group);
        JobDetail jobDetail = this.scheduler.getJobDetail(jobKey);
        if (jobDetail == null) {
            return;
        }

        this.scheduler.deleteJob(jobKey);
    }
}

3.4 TaskContext

import com.hz.job.bean.TaskConst;
import com.hz.job.bean.TaskDto;
import com.hz.job.mapper.TaskMapper;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author pp_lan
 * @date 2024/1/2
 */
@Component
@DependsOn(value = {"quartzManager"})
public class TaskContext {

    private static final Logger LOGGER = LoggerFactory.getLogger(TaskContext.class);

    @Autowired
    private QuartzManager quartzManager;

    @Autowired
    private TaskMapper taskMapper;

    @Scheduled(fixedRate = 3000)
    public void update() {
        try {

            List taskDtos = taskMapper.taskInfo();
            for (TaskDto taskDto : taskDtos) {
                this.quartzManager.startJobTask(taskDto.getTaskName(), TaskConst.GROUP.SYS.name(), taskDto.getCron());
            }
        } catch (SchedulerException e) {
            LOGGER.error("初始化定时任务异常", e);
        }
    }
}

3.5 TaskMapper

import com.hz.job.bean.TaskDto;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface TaskMapper {

    @Select("select id, task_name, cron from t_scheduler where is_delete = 0")
    List taskInfo();
}

3.6 任务示例

3.6.1 SimpleTask

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * @author pp_lan
 * @date 2024/1/2
 */
@Component
public class SimpleTask implements Job {

    private static final Logger LOGGER = LoggerFactory.getLogger(SimpleTask.class);

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        LOGGER.info("执行简单任务");
    }
}

3.6.2 WeatherTask

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * @author pp_lan
 * @date 2024/1/2
 */
@Component
public class WeatherTask implements Job {

    private static final Logger LOGGER = LoggerFactory.getLogger(WeatherTask.class);

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        LOGGER.info("天气预报");
    }
}

3.7 其他类

/**
 * @author pp_lan
 * @date 2024/1/2
 */
public class TaskConst {

    public enum GROUP {
        SYS;
    }
}
/**
 * @author pp_lan
 * @date 2024/1/2
 */
public class TaskDto {

    private int id;

    private String taskName;

    private String cron;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTaskName() {
        return taskName;
    }

    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }

    public String getCron() {
        return cron;
    }

    public void setCron(String cron) {
        this.cron = cron;
    }

    @Override
    public String toString() {
        return "TaskDto{" +
                "id=" + id +
                ", taskName='" + taskName + '\'' +
                ", cron='" + cron + '\'' +
                '}';
    }
}

4. 相关数据库表及数据

CREATE TABLE public.t_scheduler (
	id int4 NOT NULL,
	task_name varchar(32) NOT NULL,
	cron varchar(32) NOT NULL,
	is_delete int2 NOT NULL DEFAULT 0,
	CONSTRAINT pk_t_scheduler_id PRIMARY KEY (id)
);


INSERT INTO public.t_scheduler (id,task_name,cron,is_delete) VALUES
	 (1,'SimpleTask','0/4 * * * * ?',0),
	 (2,'WeatherTask','0/4 * * * * ?',0);

5. 执行结果

5.1 结果截图

SpringBoot整合quartz完成定时任务执行配置热修改_第1张图片

5.2 说明

  • 在修改数据库配置的cron后支持动态更改执行周期;

  • 3.3QuartzManager中init方法,配置了定时任务不执行的时间段。因此,截图中整分时间(2024-01-02 15:00:00)没有执行这两个任务。        

你可能感兴趣的:(spring,spring,java,sql)