一、介绍

  • 简单高效。Quartz用起来非常简单。你只要写一个继承org.quartz.Job的job类,将逻辑写入execute()方法,并将这个类设定好时间,之后Quartz就能自动帮你监控这个任务,到了指定时间自动执行,就不再需要你管这些任务了。
  • 容错。 如果其中的一个节点挂了,并不会影响其他节点上面任务的执行。
  • 支持分布式

二、主要API

  • Scheduler - 与调度器交互的主要API。
  • Job - 需要被调度器调度的任务必须实现的接口。
  • JobDetail - 用于定义任务的实例。
  • Trigger - 用于定义调度器何时调度任务执行的组件。
  • JobBuilder - 用于定义或创建JobDetail的实例 。
  • TriggerBuilder - 用于定义或创建触发器实例。

三、数据库配置

备注:也可不使用数据库,但是使用数据库能实现动态配置。

-- auto-generated definition
create table t_bi_quartz_task_config
(
    id          int auto_increment comment '配置Id'
        primary key,
    task_code   varchar(32)                          not null comment '任务编码',
    cron        varchar(20)                          not null comment 'cron表达式',
    class_name  varchar(255)                         not null comment 'job引用地址',
    description varchar(255)                         null comment '描述',
    del_flag    tinyint(1) default 0                 not null comment '删除状态(0:正常; 1:已删除)',
    create_by   varchar(32)                          null comment '创建人',
    create_time datetime   default CURRENT_TIMESTAMP null comment '创建时间',
    update_by   varchar(32)                          null comment '更新人',
    update_time datetime   default CURRENT_TIMESTAMP null comment '更新时间'
)
    comment '定时任务配置' charset = utf8mb4;

create index idx_task_code
    on t_bi_quartz_task_config (task_code);

四、项目整合

1、Mybatis Plus生成的文件

/**
 * 定时任务配置
 * @TableName t_bi_quartz_task_config
 */
@TableName(value ="t_bi_quartz_task_config")
@Data
public class BiQuartzTaskConfig implements Serializable {
    /**
     * 配置Id
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 任务编码
     */
    @TableField(value = "task_code")
    private String taskCode;

    /**
     * cron表达式
     */
    @TableField(value = "cron")
    private String cron;

    /**
     * job引用地址
     */
    @TableField(value = "class_name")
    private String className;

    /**
     * 描述
     */
    @TableField(value = "description")
    private String description;

    /**
     * 删除状态(0:正常; 1:已删除)
     */
    @TableField(value = "del_flag")
    private Integer delFlag;

    /**
     * 创建人
     */
    @TableField(value = "create_by")
    private String createBy;

    /**
     * 创建时间
     */
    @TableField(value = "create_time")
    private Date createTime;

    /**
     * 更新人
     */
    @TableField(value = "update_by")
    private String updateBy;

    /**
     * 更新时间
     */
    @TableField(value = "update_time")
    private Date updateTime;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}
/**
* 定时任务配置
* @author He PanFu
* @date 2022-04-12 17:51:11
* @description 针对表【t_bi_quartz_task_config(定时任务配置)】的数据库操作Service
*/
public interface IBiQuartzTaskConfigService extends IService<BiQuartzTaskConfig> {

    /**
     * 获取所有定时任务配置
     * @return 定时任务配置集合
     */
    List<BiQuartzTaskConfig> listBiQuartzTaskConfigAll();

    /**
     * 根据任务编码获取任务配置
     *
     * @param taskCode 任务编码
     * @return 任务配置
     */
    BiQuartzTaskConfig getBiQuartzTaskConfigByCode(String taskCode);
}
/**
 * 定时任务配置
 *
 * @author He PanFu
 * @date 2022-04-12 17:51:11
 * @description 针对表【t_bi_quartz_task_config(定时任务配置)】的数据库操作Service实现
 */
@Service
@RequiredArgsConstructor
public class BiQuartzTaskConfigServiceImpl extends ServiceImpl<BiQuartzTaskConfigMapper, BiQuartzTaskConfig>
        implements IBiQuartzTaskConfigService {

    private final BiQuartzTaskConfigMapper biQuartzTaskConfigMapper;

    @Override
    public List<BiQuartzTaskConfig> listBiQuartzTaskConfigAll() {
        return biQuartzTaskConfigMapper.listBiQuartzTaskConfigAll();
    }

    @Override
    public BiQuartzTaskConfig getBiQuartzTaskConfigByCode(String taskCode) {
        return biQuartzTaskConfigMapper.getBiQuartzTaskConfigByCode(taskCode);
    }
}
/**
 * 定时任务配置
 *
 * @author He PanFu
 * @date 2022-04-12 17:51:11
 * @description 针对表【t_bi_quartz_task_config(定时任务配置)】的数据库操作Mapper
 * @entity BiQuartzTaskConfig
 */
public interface BiQuartzTaskConfigMapper extends BaseMapper<BiQuartzTaskConfig> {

    /**
     * 获取所有定时任务配置
     *
     * @return 定时任务配置集合
     */
    List<BiQuartzTaskConfig> listBiQuartzTaskConfigAll();

    /**
     * 根据任务编码获取任务配置
     *
     * @param taskCode 任务编码
     * @return 任务配置
     */
    BiQuartzTaskConfig getBiQuartzTaskConfigByCode(String taskCode);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.system.modules.bi.mapper.BiQuartzTaskConfigMapper">

    <resultMap id="BaseResultMap" type="com.system.modules.bi.entity.BiQuartzTaskConfig">
        <id property="id" column="id" jdbcType="INTEGER"/>
        <result property="taskCode" column="task_code" jdbcType="VARCHAR"/>
        <result property="cron" column="cron" jdbcType="VARCHAR"/>
        <result property="className" column="class_name" jdbcType="VARCHAR"/>
        <result property="description" column="description" jdbcType="VARCHAR"/>
        <result property="delFlag" column="del_flag" jdbcType="TINYINT"/>
        <result property="createBy" column="create_by" jdbcType="VARCHAR"/>
        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
        <result property="updateBy" column="update_by" jdbcType="VARCHAR"/>
        <result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
    </resultMap>

    <sql id="Base_Column_List">
        id
        ,task_code,cron,
        class_name,description,del_flag,
        create_by,create_time,update_by,
        update_time
    </sql>
    <select id="listBiQuartzTaskConfigAll"
            resultType="com.system.modules.bi.entity.BiQuartzTaskConfig">
        select task_code, cron, class_name, description
        from t_bi_quartz_task_config
        where del_flag = 0
    </select>
    <select id="getBiQuartzTaskConfigByCode"
            resultType="com.system.modules.bi.entity.BiQuartzTaskConfig">
        select task_code, cron, class_name, description
        from t_bi_quartz_task_config
        where task_code = #{taskCode}
          and del_flag = 0
    </select>
</mapper>

2、导包

<!--quartz和springboot的整合包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

3、自动注入工厂创建

注:只有有这个配置,quartz才可以使用自动注入,不然quartz不会注入到Spring容器中。

/**
 * 当配置这个config后,quartz才可以使用autowired注入
 *
 * @author He PanFu
 * @date 2022-03-24 13:48:47
 */
@Component
public class QuartzScheduleAutoBeanFactory extends SpringBeanJobFactory implements ApplicationContextAware {
    private transient AutowireCapableBeanFactory beanFactory;
    @Override
    public void setApplicationContext(final ApplicationContext context) {
        beanFactory = context.getAutowireCapableBeanFactory();
    }
    @Override
    protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
        final Object job = super.createJobInstance(bundle);
        beanFactory.autowireBean(job);
        return job;
    }
}
/**
 *
 *
 * @author He PanFu
 * @date 2022-03-24 13:48:47
 */
@Configuration
public class QuartzScheduleJobConfig {

    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * 配置SchedulerFactoryBean,将一个方法产生为Bean并交给Spring容器管理
     * @param jobFactory 任务工厂
     * @return SchedulerFactoryBean
     */
    @Bean("schedulerFactoryBean")
    public SchedulerFactoryBean createFactoryBean(JobFactory jobFactory) {
        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
        factoryBean.setJobFactory(jobFactory);
        factoryBean.setTaskExecutor(threadPoolTaskExecutor);
        factoryBean.setOverwriteExistingJobs(true);
        return factoryBean;
    }

    /**
     * 通过这个类对定时任务进行操作
     *
     * @param factoryBean 工厂
     * @return 定时任务
     */
    @Bean
    public Scheduler scheduler(@Qualifier("schedulerFactoryBean") SchedulerFactoryBean factoryBean) {
        return factoryBean.getScheduler();
    }
}

4、Quartz管理 工具类

/**
 * Quartz管理 工具类
 *
 * @author He PanFu
 * @date 2022-03-23 16:19:33
 */
@Slf4j
@Component
public class QuartzManagerUtil implements ApplicationContextAware {

    private static Scheduler scheduler;
    private static IBiQuartzTaskConfigService biQuartzTaskConfigService;

    /**
     * 程序启动开始加载所有定时任务
     */
    public static void initAllJob() {
        log.info("程序启动开始加载所有定时任务");
        final List<BiQuartzTaskConfig> taskConfigAll = biQuartzTaskConfigService.listBiQuartzTaskConfigAll();

        if (CollectionUtils.isEmpty(taskConfigAll)) {
            log.info("定时任务加载数据为空");
            return;
        }

        for (BiQuartzTaskConfig taskConfig : taskConfigAll) {
            CronTrigger cronTrigger = null;
            JobDetail jobDetail = null;
            try {
                cronTrigger = getCronTrigger(taskConfig);
                jobDetail = getJobDetail(taskConfig);
                scheduler.scheduleJob(jobDetail, cronTrigger);
                log.info("编码:{}定时任务加载成功", taskConfig.getTaskCode());
            } catch (Exception e) {
                log.error("编码:{}定时任务加载失败", taskConfig.getTaskCode());
            }

        }
    }

    /**
     * 停止任务
     *
     * @param taskCode 任务编码
     */
    public static void stopJob(String taskCode) throws SchedulerException {
        log.info("停止任务:{}", taskCode);
        scheduler.pauseJob(JobKey.jobKey(taskCode));
    }

    /**
     * 恢复任务
     *
     * @param taskCode 任务编码
     * @throws SchedulerException 异常
     */
    public static void resumeJob(String taskCode) throws SchedulerException {
        log.info("恢复任务:{}", taskCode);
        scheduler.resumeJob(JobKey.jobKey(taskCode));
    }

    /**
     * 移除任务
     *
     * @param taskCode 任务编码
     */
    public static void removeJob(String taskCode) throws SchedulerException {
        log.info("移除任务:{}", taskCode);
        // 获取以前的触发器
        TriggerKey triggerKey = TriggerKey.triggerKey(taskCode);
        // 停止触发器
        scheduler.pauseTrigger(triggerKey);
        // 删除触发器
        scheduler.unscheduleJob(triggerKey);
        // 删除任务
        scheduler.deleteJob(JobKey.jobKey(taskCode));
    }

    /**
     * 重新加载任务
     *
     * @param taskCode 任务编码
     * @throws Exception 异常
     */
    public static void reload(String taskCode) throws Exception {
        log.info("重新加载任务:{}", taskCode);
        final BiQuartzTaskConfig taskConfig = biQuartzTaskConfigService.getBiQuartzTaskConfigByCode(taskCode);
        if (taskConfig == null) {
            log.error("未找到相关Job配置,任务编码:{}", taskCode);
            throw new SchedulerConfigException("未找到相关Job配置");
        }
        // 获取以前的触发器
        TriggerKey triggerKey = TriggerKey.triggerKey(taskCode);
        // 停止触发器
        scheduler.pauseTrigger(triggerKey);
        // 删除触发器
        scheduler.unscheduleJob(triggerKey);
        // 删除原来的job
        scheduler.deleteJob(JobKey.jobKey(taskCode));

        JobDetail jobDetail = getJobDetail(taskConfig);
        CronTrigger cronTrigger = getCronTrigger(taskConfig);
        // 重新加载job
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }

    /**
     * 添加新任务
     *
     * @param taskCode 任务编码
     * @throws SchedulerConfigException 异常
     */
    public static void addNewJob(String taskCode) throws SchedulerConfigException {
        log.info("添加新任务:{}", taskCode);
        final BiQuartzTaskConfig taskConfig = biQuartzTaskConfigService.getBiQuartzTaskConfigByCode(taskCode);
        if (taskConfig == null) {
            log.error("未找到相关Job配置,任务编码:{}", taskCode);
            throw new SchedulerConfigException("未找到相关Job配置");
        }
        try {
            JobDetail jobDetail = getJobDetail(taskConfig);
            CronTrigger cronTrigger = getCronTrigger(taskConfig);
            scheduler.scheduleJob(jobDetail, cronTrigger);
        } catch (Exception e) {
            log.error("加载定时任务异常", e);
            throw new SchedulerConfigException("加载定时任务异常", e);
        }
    }

    /**
     * 获取任务是否存在
     * <p>
     * STATE_BLOCKED 4 阻塞
     * STATE_COMPLETE 2 完成
     * STATE_ERROR 3 错误
     * STATE_NONE -1 不存在
     * STATE_NORMAL 0 正常
     * STATE_PAUSED 1 暂停
     *
     * @param taskCode 任务编码
     */
    public static Boolean notExists(String taskCode) {
        log.info("获取任务是否存在:{}", taskCode);
        try {
            return scheduler.getTriggerState(TriggerKey.triggerKey(taskCode)) == Trigger.TriggerState.NONE;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 组装JobDetail
     *
     * @param taskConfig 任务配置
     * @return CronTrigger
     * @throws ClassNotFoundException 异常
     */
    private static JobDetail getJobDetail(BiQuartzTaskConfig taskConfig) throws ClassNotFoundException {
        Class<? extends Job> aClass = Class.forName(taskConfig.getClassName()).asSubclass(Job.class);
        return JobBuilder.newJob()
                .withIdentity(JobKey.jobKey(taskConfig.getTaskCode()))
                .withDescription(taskConfig.getDescription())
                .ofType(aClass).build();
    }

    /**
     * 组装CronTrigger
     *
     * @param taskConfig 任务配置
     * @return CronTrigger
     */
    private static CronTrigger getCronTrigger(BiQuartzTaskConfig taskConfig) {
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(taskConfig.getCron());
        return TriggerBuilder.newTrigger()
                .withIdentity(TriggerKey.triggerKey(taskConfig.getClassName()))
                .withSchedule(cronScheduleBuilder)
                .build();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        scheduler = (Scheduler) applicationContext.getBean("scheduler");
        biQuartzTaskConfigService = applicationContext.getBean(IBiQuartzTaskConfigService.class);
    }
}

5、测试

/**
 * @author He PanFu
 * @date 2021-10-14 14:34:57
 */
@RequestMapping("/test/quartz")
@RestController
public class QuartzController {

    @GetMapping("/task/{taskCode}/{type}")
    public String task(@PathVariable("taskCode") String taskCode, @PathVariable("type") Integer type) throws Exception {
        if (1 == type) {
            QuartzManagerUtil.initAllJob();
        } else if (2 == type) {
            QuartzManagerUtil.stopJob(taskCode);
        } else if (3 == type) {
            QuartzManagerUtil.resumeJob(taskCode);
        } else if (4 == type) {
            QuartzManagerUtil.removeJob(taskCode);
        } else if (5 == type) {
            QuartzManagerUtil.reload(taskCode);
        } else if (6 == type) {
            QuartzManagerUtil.addNewJob(taskCode);
        }

        return "task";
    }
}