Springboot整合Quartz定时任务

使用

以下使用maven作为依赖

添加依赖

<!-- springboot quartz-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

spring配置文件

spring:
  #定时任务配置
  quartz:
    job-store-type: jdbc
    properties:
      org:
        quartz:``````````````
          scheduler:
            instanceName: quartzScheduler
            instanceId: AUTO
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: QRTZ_
            #自定义的数据源别名,与最下方一致
            dataSource: qrtzDS
            #与cron的调度有关,忽略重启期间的任务
            misfireThreshold: 10000
            #设置为true开启集群
            isClustered: false
            clusterCheckinInterval: 10000
            #如果开启,会没法给任务传参数
            useProperties: false
          dataSource:
            qrtzDS:
              driver: com.mysql.cj.jdbc.Driver
              URL: jdbc:mysql://127.0.0.1:3306/qrtz?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
              user: user
              password: 123456
              maxConnections: 10
              provider: hikaricp
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 20
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: true

代码实现

sql

create table sas_quartz_job
(
    job_id          bigint default 0          not null comment '任务id'
        primary key,
    job_name        varchar(200)              null comment '任务名称',
    bean_name       varchar(200) charset utf8 null comment '类',
    method_name     varchar(100) charset utf8 null comment '方法',
    params          text charset utf8         null comment '参数',
    cron_expression varchar(100) charset utf8 null comment '表达式',
    status          tinyint                   null comment '任务状态,0:暂停,1:正常, 2:已执行',
    remark          varchar(200) charset utf8 null comment '备注',
    create_by       varchar(200) charset utf8 null comment '创建人',
    create_time     datetime                  null comment '创建时间',
    update_by       varchar(200) charset utf8 null comment '更新人',
    update_time     datetime                  null comment '更新时间',
    is_delete       tinyint(1)                null comment '删除标记',
    source_id       bigint(19)                null comment '谁的主键关联的该定时任务'
);

石英日程配置

@Configuration
public class QuartzScheduleConfig {
    @Resource
    QuartzJobService quartzJobService;
    @Resource
    private SchedulerFactoryBean schedulerFactoryBean;

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

    /**
     * 项目启动,初始化任务
     * 1. 从数据库中加载存在的,可执行的定时任务
     * 2. 清除僵尸任务,如果当前定时任务的cron表达式不足以执行一次,那么就干掉这个任务
     * 系统可能存在一次性的定时任务,该种任务已创建,但是由于部分情况(如:定时服务重启或关闭,导致定时任务未执行的情况)导致任务并不能正常执行,
     * 这个时候问题来了,这些任务可能一直躺在数据库中并不会被执行,但每次都会被加载到内存中,
     * 所以,这里需要清理掉这些不会被再次运行的任务
     */
    @PostConstruct
    public void init() throws ParseException {
        List<QuartzJob> jobList = quartzJobService.list(new QuartzJob());
        List<Long> preRemovedJobIds = new ArrayList<>();
        for (QuartzJob job : jobList) {
            /*判断当前cron表达式是否至少会执行一次,会则创建定时任务,不会则删除定时任务*/
            if (new CronExpression(job.getCronExpression()).getTimeAfter(new Date()) != null) {
                CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(job.getJobId());
                //如果不存在,则创建
                if (cronTrigger == null) {
                    ScheduleUtils.createScheduleJob(job);
                } else {
                    ScheduleUtils.updateScheduleJob(job);
                }
            } else {
                preRemovedJobIds.add(job.getJobId());
            }
        }
        if (preRemovedJobIds.size() > 0) {
            QuartzJob job = new QuartzJob();
            job.setJobIds(preRemovedJobIds.toArray(new Long[preRemovedJobIds.size()]));
            quartzJobService.removeByJobId(job);
        }
    }
}

实体类(QuartzJob,石英任务):

@Data
@TableName(value = "quartz_job")
public class QuartzJob {
    /**
     * 任务调度参数key
     */
    public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";

    @TableId(value = "job_id", type = IdType.ASSIGN_ID)
    @Schema(description = "任务id")
    private Long jobId;
    
    @Schema(description = "任务名称")
    @TableField(value = "job_name")
    private String jobName;

    @Schema(description = "谁的主键关联的该定时任务")
    @TableField(value = "source_id")
    private Long sourceId;

    @TableField(value = "bean_name")
    @Schema(description = "类")
    private String beanName;

    @TableField(value = "method_name")
    @Schema(description = "方法")
    private String methodName;

    @TableField(value = "params")
    @Schema(description = "参数")
    private String params;

    @TableField(value = "cron_expression")
    @Schema(description = "表达式")
    private String cronExpression;

    @TableField(value = "status")
    @Schema(description = "任务状态,0:暂停,1:正常")
    private Integer status;

    @TableField(value = "remark")
    @Schema(description = "备注")
    private String remark;

    @TableField(value = "create_by", fill = FieldFill.INSERT)
    @Schema(description = "创建人")
    private String createBy;

    @TableField(value = "create_time", fill = FieldFill.INSERT)
    @Schema(description = "创建时间")
    private LocalDateTime createTime;

    @TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
    @Schema(description = "更新人")
    private String updateBy;

    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
    @Schema(description = "更新时间")
    private LocalDateTime updateTime;

    @TableField(value = "is_delete", fill = FieldFill.INSERT)
    @Schema(description = "删除标记")
    private Boolean deleted;

}

mapper(QuartzJobMapper,石英任务数据访问对象):

@Mapper
public interface QuartzJobMapper extends BasicMapper<QuartzJob> {

    @MapKey("id")
    IPage<Map<String, Object>> pageByQuery(IPage<?> page, @Param(Constants.WRAPPER) Wrapper<QuartzJob> wrapper);

}

service (QuartzJobService,石英任务服务对象):

public interface QuartzJobService extends IService<QuartzJob> {
    /**
      * 插入数据
      *
      * @param entity
      * @return
      */
    int insert(QuartzJob job);

    /**
      * 更新数据
      *
      * @param entity
      * @return
      */
    int update(QuartzJob job);

    /**
      * 根据id获取数据
      *
      * @param entity
      * @return
      */
    QuartzJob detailByJobId(QuartzJob job);

    /**
      * 获取分页数据
      *
      * @param entity
      * @return
      */
    IPage<QuartzJob> page(QuartzJob job);

    /**
      * 查询列表
      *
      * @param entity
      * @return
      */
    List<QuartzJob> list(QuartzJob job);

    /**
      * 删除数据
      *
      * @param entity
      * @return
      */
    boolean removeByJobId(QuartzJob job);


    /**
     * 分页查询任务
     * @param entity 查询参数对象
     * @return 分页对象
     */
    @MapKey("id")
    IPage<Map<String,Object>> pageByQuery(QuartzJob job);

    /**
     * 新增任务
     * @param job 要新增的定时任务对象
     * @return 是否保存成功
     */
    boolean saveQuartzJob(QuartzJob job);

    /**
     * 根据id查询任务
     * @param jobId 要查询的任务ID
     * @return 定时任务对象
     */
    QuartzJob getQuartzJobById(Long jobId);

    /**
     * 修改任务
     * @param job 要更新的定时任务对象
     * @return 是否更新成功
     */
    boolean updateQuartzJob(QuartzJob job);

    /**
     * 批量删除任务
     * @param id 要批量删除的定时任务ID列表
     * @return 已删除数量
     */
    int batchRemoveQuartzJob(Long[] id);

    /**
     * 立即运行任务
     * @param id 要执行定时任务的ID列表
     */
    void run(Long[] id);

    /**
     * 暂停任务
     * @param id 要暂停的任务ID列表
     * @return 是否暂停成功
     */
    boolean pause(Long[] id);

    /**
     * 恢复任务
     * @param id 要恢复的定时任务ID列表
     * @return 是否恢复成功
     */
    boolean resume(Long[] id);

}

实现类(QuartzJobService,石英任务服务实现对象):

@Service
public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob> implements QuartzJobService {

    @Resource
    QuartzJobMapper quartzJobMapper;

    @Resource
    private SnowflakeIdWorker snowflakeIdWorker;


    public int insert(QuartzJob entity) {
        entity.setJobId(snowflakeIdWorker.nextId());
        entity.setCreateBy(LoginUtil.getLoginUser().getRealname());
        entity.setCreateTime(LocalDateTime.now());
        return quartzJobMapper.insert(entity);
    }

    @Override
    public int update(QuartzJob entity) {
        QueryWrapper<QuartzJob> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("job_id", entity.getJobId());
        queryWrapper.eq("is_delete", 0);
        return quartzJobMapper.update(entity, queryWrapper);
    }

    @Override
    public QuartzJob detailByJobId(QuartzJob entity) {
        QueryWrapper<QuartzJob> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("job_id", entity.getJobId());
        queryWrapper.eq("is_delete", 0);
        QuartzJob result = quartzJobMapper.selectOne(queryWrapper);
        if (entity == null) {
            return null;
        }
        return result;
    }

    @Override
    public List<QuartzJob> list(QuartzJob entity) {
        List<QuartzJob> list = quartzJobMapper.list();
        return list;
    }

    @Override
    public IPage<QuartzJob> page(QuartzJob entity) {
        LambdaQueryWrapper<QuartzJob> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StringUtils.isNotBlank(entity.getBeanName()), QuartzJob::getBeanName, entity.getBeanName())
                .eq(StringUtils.isNotBlank(entity.getMethodName()), QuartzJob::getMethodName, entity.getMethodName())
                .eq(entity.getStatus() != null, QuartzJob::getStatus, entity.getStatus())
                .ge(StringUtils.isNotBlank(entity.getStartDate()), QuartzJob::getCreateTime, entity.getStartDate())
                .le(StringUtils.isNotBlank(entity.getEndDate()), QuartzJob::getCreateTime, entity.getEndDate());
        IPage<Map<String, Object>> iPage = quartzJobMapper.pageByQuery(entity.createPage(), wrapper);
        return iPage;
    }

    @Override
    public boolean removeByJobId(QuartzJob entity) {
        for (Long jobId : entity.getJobIds()) {
            ScheduleUtils.deleteScheduleJob(jobId);
        }
        return quartzJobMapper.deleteBatchIds(Arrays.asList(entity.getJobIds())) > 0;
    }


    /**
     * 分页查询任务
     *
     * @param entity 定时任务查询对象
     * @return 分页Map对象
     */
    @Override
    public IPage<Map<String, Object>> pageByQuery(QuartzJob entity) {
        LambdaQueryWrapper<QuartzJob> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(entity.getStatus() != null, QuartzJob::getJobId, entity.getJobId())
                .eq(entity.getJobFlag() != null, QuartzJob::getJobFlag, entity.getJobFlag())
                .eq(StringUtils.isNotBlank(entity.getBeanName()), QuartzJob::getBeanName, entity.getBeanName())
                .eq(StringUtils.isNotBlank(entity.getMethodName()), QuartzJob::getMethodName, entity.getMethodName())
                .eq(entity.getStatus() != null, QuartzJob::getStatus, entity.getStatus())
                .ge(StringUtils.isNotBlank(entity.getStartDate()), QuartzJob::getCreateTime, entity.getStartDate())
                .le(StringUtils.isNotBlank(entity.getEndDate()), QuartzJob::getCreateTime, entity.getEndDate());
        return quartzJobMapper.pageByQuery(entity.createPage(), wrapper);
    }

    /**
     * 新增任务
     *
     * @param job 任务对象
     * @return 是否成功
     */
    @Override
    public boolean saveQuartzJob(QuartzJob job) {
        job.setJobId(job.getJobId() == null ? snowflakeIdWorker.nextId() : job.getJobId());
        job.setStatus(QuartzJobStatusEnum.NORMAL.getValue());
        QuartzJob QuartzJob = BeanCopyUtil.copyProperties(job, QuartzJob::new);
        QuartzJob.setCreateBy(LoginUtil.getLoginUser().getRealname());
        QuartzJob.setCreateTime(LocalDateTime.now());
        QuartzJob.setDeleted(false);
        int count = quartzJobMapper.insert(QuartzJob);
        ScheduleUtils.createScheduleJob(job);
        return count > 0;
    }

    /**
     * 根据id查询任务
     *
     * @param jobId 任务ID
     * @return 任务对象
     */
    @Override
    public QuartzJob getQuartzJobById(Long jobId) {
        QuartzJob job = quartzJobMapper.selectById(jobId);
        return BeanCopyUtil.copyProperties(job, QuartzJob::new);
    }

    /**
     * 更新任务
     *
     * @param entity 任务对象
     * @return 是否成功
     */
    @Override
    public boolean updateQuartzJob(Sasentity entity) {
        QuartzJob jobDO = new QuartzJob();
        jobDO.setJobId(entity.getJobId());
        jobDO.setSourceId(entity.getSourceId());
        jobDO.setStatus(entity.getStatus());
        jobDO.setBeanName(entity.getBeanName());
        jobDO.setMethodName(entity.getMethodName());
        jobDO.setParams(entity.getParams());
        jobDO.setCronExpression(entity.getCronExpression());
        jobDO.setUpdateBy(LoginUtil.getLoginUser().getRealname());
        jobDO.setUpdateTime(LocalDateTime.now());
        int count = quartzJobMapper.update(jobDO, new LambdaQueryWrapper<QuartzJob>().eq(QuartzJob::getJobId, entity.getJobId()));
        ScheduleUtils.updateScheduleJob(entity);
        return count > 0;
    }

    /**
     * 批量删除任务
     *
     * @param id 任务主键数组
     * @return 删除数量
     */
    @Override
    public int batchRemoveQuartzJob(Long[] id) {
        for (Long jobId : id) {
            ScheduleUtils.deleteScheduleJob(jobId);
        }
        return quartzJobMapper.deleteBatchIds(Arrays.asList(id));
    }

    /**
     * 立即运行任务
     *
     * @param id 任务主键数组
     */
    @Override
    public void run(Long[] id) {
        for (Long jobId : id) {
            ScheduleUtils.run(quartzJobMapper.selectById(jobId));
        }
    }

    /**
     * 暂停任务
     *
     * @param id 任务主键数组
     * @return 是否成功
     */
    @Override
    public boolean pause(Long[] id) {
        for (Long jobId : id) {
            ScheduleUtils.pauseJob(jobId);
        }
        QuartzJob QuartzJob = new QuartzJob();
        QuartzJob.setStatus(QuartzJobStatusEnum.PAUSE.getValue());
        LambdaQueryWrapper<QuartzJob> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(id.length > 0, QuartzJob::getJobId, id);
        int updates = quartzJobMapper.update(QuartzJob, wrapper);
        return updates > 0;
    }

    /**
     * 恢复任务
     *
     * @param id 任务主键数组
     * @return 是否成功
     */
    @Override
    public boolean resume(Long[] id) {
        for (Long jobId : id) {
            ScheduleUtils.resumeJob(jobId);
        }
        QuartzJob QuartzJob = new QuartzJob();
        QuartzJob.setStatus(QuartzJobStatusEnum.NORMAL.getValue());
        LambdaQueryWrapper<QuartzJob> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(id.length > 0, QuartzJob::getJobId, id);
        int updates = quartzJobMapper.update(QuartzJob, wrapper);
        return updates > 0;
    }

}

任务日程对象:

public class ScheduleJob extends QuartzJobBean {
    private ExecutorService service = Executors.newSingleThreadExecutor();

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        String jobStr = context.getMergedJobDataMap().getString(QuartzJob.JOB_PARAM_KEY);
        QuartzJob scheduleJob = JsonUtil.parse(jobStr, QuartzJob.class);
        //任务开始时间
        long startTime = System.currentTimeMillis();
        try {
            //执行任务
            log.info("任务准备执行,任务ID:" + scheduleJob.getJobId());
            ScheduleRunnable task = new ScheduleRunnable(scheduleJob.getBeanName(),
                    scheduleJob.getMethodName(), scheduleJob.getParams());
            Future<?> future = service.submit(task);
            future.get();
            //任务执行总时长
            long times = System.currentTimeMillis() - startTime;
            //如果是单次定时任务,执行完毕后删除该任务
            if (new CronExpression(scheduleJob.getCronExpression()).getTimeAfter(new Date())==null){
                //任务状态    0:失败    1:成功  2:已执行
                scheduleJob.setStatus(2);
                QuartzJobMapper quartzJobMapper = (QuartzJobMapper) SpringUtils.getBean("quartzJobMapper");
                quartzJobMapper.updateById(scheduleJob);
                ScheduleUtils.deleteScheduleJob(scheduleJob.getJobId());
            }
            log.info("任务执行完毕,任务ID:" + scheduleJob.getJobId() + "  总共耗时:" + times + "毫秒");
        } catch (Exception e) {
            log.error("任务执行失败,任务ID:" + scheduleJob.getJobId(), e);
            //任务执行总时长
            long times = System.currentTimeMillis() - startTime;
            jobLog.setTimes((int)times);
        }finally {
            //这里可以增加日志执行记录quartzJobLogMapper.save(jobLog);
        }
    }

日程工具类(ScheduleUtils):

@DependsOn("springUtils")
public class ScheduleUtils {

    private static Scheduler scheduler = (Scheduler) SpringUtils.getBean("scheduler");

    private final static String JOB_NAME = "TASK_";

    /**
     * 获取触发器key
     *
     * @param jobId 任务主键
     * @return 触发器Key
     */
    public static TriggerKey getTriggerKey(Long jobId) {
        return TriggerKey.triggerKey(JOB_NAME + jobId);
    }

    /**
     * 获取jobKey
     *
     * @param jobId 任务主键
     * @return 任务Key
     */
    public static JobKey getJobKey(Long jobId) {
        return JobKey.jobKey(JOB_NAME + jobId);
    }

    /**
     * 获取表达式触发器
     *
     * @param jobId 任务主键
     * @return 触发器
     */
    public static CronTrigger getCronTrigger(Long jobId) {
        try {
            return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
        } catch (SchedulerException e) {
            throw new ServiceException(ErrorEnum.CRON_TRIGGER_NOT_FOUND_EXCEPTION);
        }
    }

    /**
     * 创建定时任务
     *
     * @param scheduleJob 定时任务对象
     */
    public static void createScheduleJob(QuartzJob scheduleJob) {
        try {
            /*当前cron表达式至少会执行一次*/
            if (new CronExpression(scheduleJob.getCronExpression()).getTimeAfter(new Date()) != null) {
                //构建job信息
                JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getJobId())).build();

                //表达式调度构建器
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
                        .withMisfireHandlingInstructionDoNothing();

                //按新的cronExpression表达式构建一个新的trigger
                CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getJobId())).withSchedule(scheduleBuilder).build();
                //放入参数,运行时的方法可以获取
                jobDetail.getJobDataMap().put(SasQuartzJobDTO.JOB_PARAM_KEY, JsonUtil.stringify(scheduleJob));
                //scheduler.clear();
                scheduler.scheduleJob(jobDetail, trigger);
                //暂停任务
                if (scheduleJob.getStatus() == QuartzJobStatusEnum.PAUSE.getValue()) {
                    pauseJob(scheduleJob.getJobId());
                }
            } else {
                throw new ServiceException(ErrorEnum.SCHEDULE_JOB_CREATE_EXCEPTION);
            }
        } catch (SchedulerException e) {
            throw new ServiceException(ErrorEnum.SCHEDULE_JOB_CREATE_EXCEPTION);
        } catch (ParseException e) {
            throw new ServiceException(ErrorEnum.SCHEDULE_JOB_CRON_EXPRESSION_PARSE_EXCEPTION);
        }
    }

    /**
     * 更新定时任务
     *
     * @param scheduleJob 定时任务对象
     */
    public static void updateScheduleJob(SasQuartzJobDTO scheduleJob) {
        try {
            if (new CronExpression(scheduleJob.getCronExpression()).getTimeAfter(new Date()) != null) {
                TriggerKey triggerKey = getTriggerKey(scheduleJob.getJobId());
                //表达式调度构建器
                CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
                        .withMisfireHandlingInstructionDoNothing();

                CronTrigger trigger = getCronTrigger(scheduleJob.getJobId());

                //如果生成的trigger只是执行单次的,那么在执行完一次以后则会找不到该trigger,这个时候会出现空指针异常
                if (trigger == null) {
                    throw new ServiceException(ErrorEnum.SCHEDULE_JOB_TRIGGER_NULL_EXCEPTION);
                }

                //按新的cronExpression表达式重新构建trigger
                trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

                //参数
                trigger.getJobDataMap().put(SasQuartzJobDTO.JOB_PARAM_KEY, JsonUtil.stringify(scheduleJob));

                scheduler.rescheduleJob(triggerKey, trigger);

                //暂停任务
                if (scheduleJob.getStatus() == QuartzJobStatusEnum.PAUSE.getValue()) {
                    pauseJob(scheduleJob.getJobId());
                }
            }
        } catch (SchedulerException e) {
            throw new ServiceException(ErrorEnum.SCHEDULE_JOB_UPDATE_EXCEPTION);
        } catch (ParseException e) {
            throw new ServiceException(ErrorEnum.SCHEDULE_JOB_CRON_EXPRESSION_PARSE_EXCEPTION);
        }
    }

    /**
     * 立即执行任务
     *
     * @param scheduleJob 定时任务对象
     */
    public static void run(SasQuartzJobDO scheduleJob) {
        try {
            //参数
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(SasQuartzJobDTO.JOB_PARAM_KEY, JsonUtil.stringify(scheduleJob));

            scheduler.triggerJob(getJobKey(scheduleJob.getJobId()), dataMap);
        } catch (SchedulerException e) {
            throw new ServiceException(ErrorEnum.SCHEDULE_JOB_RUN_EXCEPTION);
        }
    }

    /**
     * 暂停任务
     *
     * @param jobId 任务主键
     */
    public static void pauseJob(Long jobId) {
        try {
            scheduler.pauseJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new ServiceException(ErrorEnum.SCHEDULE_JOB_PAUSE_EXCEPTION);
        }
    }

    /**
     * 恢复任务
     *
     * @param jobId 任务主键
     */
    public static void resumeJob(Long jobId) {
        try {
            scheduler.resumeJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new ServiceException(ErrorEnum.SCHEDULE_JOB_RECOVERY_EXCEPTION);
        }
    }

    /**
     * 删除定时任务
     *
     * @param jobId 任务主键
     */
    public static void deleteScheduleJob(Long jobId) {
        try {
            scheduler.deleteJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new ServiceException(ErrorEnum.SCHEDULE_JOB_DELETE_EXCEPTION);
        }
    }
}

通过以上的代码 我们就可以实现通过QuartzJobService来管理我们的定时任务了,可以动态的添加定时任务,删除,暂停,及批量执行定时任务等功能。

当然 上面的代码并不完整,这里还缺少Quartz自带的11张基础表,当然这些表可以在quartz的jar包中找到,这里由于篇幅原因就不详细展开了。

下面我们在介绍一些跟定时任务相关的内容:cron表达式,我们在使用QuartzJobService创建定时任务时,必不可少的东西

Cron表达式

Cron-Expressions用于配置 CronTrigger 的实例。Cron 表达式实际上是由七个子表达式组成的字符串,它们描述了计划的各个细节。这些子表达式用空格分隔,代表:

  1. 星期几
  2. 年份(可选字段)

完整的 cron-expression 的一个示例是字符串*“0 0 12 ? * WED”* ——意思是“每周三中午 12:00:00”。

单个子表达式可以包含范围和或列表

例如,上一个示例中的星期几字段(读作“WED”)可以替换为“MON-FRI”、“MON,WED,FRI”,甚至“MON-WED,SAT”。

通配符(’ * ’ 字符)可用于表示该字段的“每个”可能值。

因此,前面示例中“Month”字段中的 * 字符仅表示“每个月”。因此,Day-Of-Week 字段中的 “*” 显然表示 “一周中的每一天”。

所有字段都有一组可以指定的有效值

这些值应该相当明显 - 例如:

  • 秒和分钟的数字 0 到 59,
  • 小时的值 0 到 23。
  • Day-of-Month 可以是 1-31 之间的任何值,但您需要注意给定月份有多少天!
  • 月份可以指定为 0 到 11 之间的值,或者使用字符串 JAN、FEB、MAR、APR、MAY、JUN、JUL、AUG、SEP、OCT、NOV 和 DEC。
  • 可以将星期几指定为 1 到 7 之间的值(1 = 星期日)或使用字符串 SUN、MON、TUE、WED、THU、FRI 和 SAT。

‘/’ 字符可用于指定值的增量。

例如,如果您在“分钟”字段中输入“0/15”,则表示“每小时的第 15 分钟,从第 0 分钟开始”。

如果您在“分钟”字段中使用“3/20”,则表示“每小时的第 20 分钟,从第三分钟开始”——换句话说,它与在“分钟”字段中指定“3、23、43”相同场地。

请注意“ /35”并不意味着“每 35 分钟”的微妙之处 - 它意味着“每小时的第 35 分钟,从零分钟开始” - 或者换句话说与指定“0,35”相同。

'?'字符为日期和星期几字段允的许字符。

它用于指定“没有特定值”。当您需要在两个字段之一而不是另一个字段中指定某些内容时,这很有用。请参阅下面的示例(和 CronTrigger JavaDoc)进行说明。

“L”字符可用于日期和星期几字段。

该字符是“last”的简写,但在两个字段中具有不同的含义。

例如,day-of-month 字段中的值“L”表示“该月的最后一天”- 1 月的第 31 天,非闰年的 2 月的第 28 天。

如果单独用于星期几字段,它仅表示“7”或“SAT”。

但如果在星期几字段中用在另一个值之后,则表示“该月的最后一个 xxx 日”——例如“6L”或“FRIL”均表示“该月的最后一个星期五”。

您还可以指定从该月最后一天开始的偏移量,例如“L-3”,这表示该日历月的倒数第三天。

使用“L”选项时,重要的是不要指定列表或值范围,因为您会得到令人困惑/意外的结果。

‘W’ 用于指定最接近给定日期的工作日(周一至周五)。

例如,如果您指定“15W”作为日期字段的值,则含义是:“离该月 15 号最近的工作日”。

‘#’ 用于指定该月的“第 n 个”XXX 工作日。

例如,星期几字段中的“6#3”或“FRI#3”的值表示“该月的第三个星期五”。

这里有更多表达式及其含义的示例 - 您可以在 org.quartz.CronExpression 的 JavaDoc 中找到更多

示例 Cron 表达式

CronTrigger 示例 1 - 用于创建每 5 分钟触发一次的触发器的表达式

“0 0/5 * * * ?”

CronTrigger 示例 2 - 创建触发器的表达式,该触发器每 5 分钟触发一次,在该分钟后的 10 秒(即上午 10:00:10、上午 10:05:10 等)触发。

“10 0/5 * * * ?”

CronTrigger 示例 3 - 用于创建触发器的表达式,该触发器在每周三和周五的 10:30、11:30、12:30 和 13:30 触发。

“0 30 10-13 ?* 周三、周五”

CronTrigger 示例 4 - 创建触发器的表达式,该触发器在每月 5 号和 20 号的上午 8 点到 10 点之间每半小时触发一次。请注意,触发器不会在上午 10:00 触发,仅在 8:00、8:30、9:00 和 9:30 触发

“0 0/30 8-9 5,20 * ?”

请注意,某些调度要求过于复杂,无法用单个触发器来表达——例如“上午 9:00 到 10:00 之间每 5 分钟一次,下午 1:00 到 10:00 之间每 20 分钟一次”。这种情况下的解决方案是简单地创建两个触发器,并注册它们以运行同一个作业。