SpringBoot集成Quartz(建表语句获取+数据持久化配置)

本文主要介绍使用SpringBoot的起步依赖和融入SpringBoot数据库连接的yml配置。

添加maven依赖

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

配置文件

配置可以直接加到 application.yml 进行自动装配,数据源会自动使用当前项目的数据源配置或者连接池配置。

具体的配置含义可以参照: https://www.w3cschool.cn/quartz_doc/quartz_doc-ml8e2d9m.html

配置值对应 spring.quartz.properties 后面的那些

spring:
  quartz:
    # 相关属性配置
    properties:
      org:
        quartz:
          scheduler:
            instanceName: clusteredScheduler
            instanceId: AUTO
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: QRTZ_
            isClustered: true
            clusterCheckinInterval: 10000
            useProperties: false
          # 线程池的参数配置
          threadPool:
            class: org.quartz.simpl.SimpleThreadPool
            threadCount: 10
            threadPriority: 5
            threadsInheritContextClassLoaderOfInitializingThread: false
          # 指定监听器的类 进行属性装配,这里我添加了一个监听器的装配类
          plugin:
            runningListener:
              class: com.netease.core.job.plugin.RunningListenerPlugin
              # 自定义属性,代码中读取,用于控制是否开启监听
              enableRunningLog: true
    # 持久化-数据库方式存储
    job-store-type: jdbc

数据库建表

使用的是2.2.3版本的 Oracle 建表语句:建表SQL文件

如果使用别的数据库,可以到GitHub上直接获取:https://github.com/quartz-scheduler/quartz

鉴于很多人不太会找,我简单说下建表语句的查找方法:

  1. 进入GitHub仓库之后,切换至Tag,找到你对应的版本;
  2. 进入切换到tag之后,点击Find file按钮, 搜索 .sql,根据文件名大概就能判断哪个是符合你需求的建表语句;
  3. 或者你知道文件的路径,可以直接找,例如:2.1.x版本的建表语句一般放在 docs/dbTables 下面,根据里面的SQL文件名就能找到你要的建表语句;

在这里插入图片描述

在这里插入图片描述

配置完启动就可以使用的了。

实际应用代码

创建定时任务的可选参数,我们以简单任务和CRON任务为例,以下DTO字段主要源于 QRTZ_TRIGGERSQRTZ_CRON_TRIGGERSQRTZ_SIMPLE_TRIGGERS 这三张表。 根据表结构不难看出,触发器的基本属性在 QRTZ_TRIGGERS中,另外两张表保存了不同类型触发器的特有参数。

TriggerDto.java 对应 QRTZ_TRIGGERS

@Getter
@Setter
public class TriggerDto {

    /**
     * 调度器名称
     */
    private String schedName;

    /**
     * 触发器名称
     */
    private String triggerName;

    /**
     * 触发器分组
     */
    private String triggerGroup;

    /**
     * Job名称
     */
    private String jobName;

    /**
     * Job分组
     */
    private String jobGroup;

    /**
     * Job描述
     */
    private String description;

    /**
     * 下次执行时间 毫秒级时间戳
     */
    private Long nextFireTime;

    /**
     * 上次执行时间 毫秒级时间戳
     */
    private Long prevFireTime;

    /**
     * 优先级
     */
    private Integer priority;

    /**
     * 触发器状态
     */
    private String triggerState;

    /**
     * 触发器类型
     */
    @NotEmpty
    private String triggerType;

    /**
     * 开始时间 毫秒级时间戳
     */
    private Long startTime;

    /**
     * 结束时间 毫秒级时间戳
     */
    private Long endTime;

    private String calendarName;

    private Integer misfireInstr;
}

TriggerCreateParam.java 包含CRON和SIMPLE任务的参数

@Getter
@Setter
public class TriggerCreateParam extends TriggerDto {

    /**
     * 实际执行任务类名
     */
    @NotEmpty
    private String jobClassName;

    /**
     * CRON表达式 针对CRON任务
     */
    private String cronExpression;

    /**
     * 开始时间
     */
    private Date start;

    /**
     * 结束时间
     */
    private Date end;

    /**
     * 重复次数 针对简单任务 0表示无限次数
     */
    private Integer repeatCount;

    /**
     * 重复时间间隔 针对简单任务
     */
    private Integer repeatInterval;

    private List<JobData> jobDataList;
    
}

通用的创建方法

    /**
     * 创建一个定时任务(触发器)
     *
     * @param triggerCreateParam 创建参数
     * @throws SchedulerException 任务调度异常
     */
	@SuppressWarnings({"rawtypes", "unchecked"})
    public void createTrigger(TriggerCreateParam triggerCreateParam) throws SchedulerException {
        String jobClassName = triggerCreateParam.getJobClassName();

        Class forName;
        try {
            forName = Class.forName(jobClassName);
            if (!org.quartz.Job.class.isAssignableFrom(Class.forName(jobClassName))) {
                throw new IllegalArgumentException("任务类需要继承 org.quartz.Job");
            }
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("任务类名不存在, 请先正确创建该执行类" + jobClassName);
        }

        JobBuilder jobBuilder = JobBuilder.newJob(forName)
                .withIdentity(triggerCreateParam.getJobName(), triggerCreateParam.getJobGroup())
                .withDescription(triggerCreateParam.getDescription());

        // 检查是否有额外的任务参数,并设置Job Data
        if (CollectionUtils.isNotEmpty(triggerCreateParam.getJobDataList())) {
            JobDataMap data = new JobDataMap();
            List<JobData> jobDataList = triggerCreateParam.getJobDataList();
            for (JobData jobData : jobDataList) {
                data.put(jobData.getName(), jobData.getValue());
            }
            jobBuilder = jobBuilder.usingJobData(data);
        }

        JobDetail jobDetail = jobBuilder.build();

        TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger()
                .withIdentity(triggerCreateParam.getTriggerName(), triggerCreateParam.getTriggerGroup()).forJob(jobDetail);
        if (triggerCreateParam.getStartTime() != null && triggerCreateParam.getStartTime() > 0) {
            triggerBuilder.startAt(new Date(triggerCreateParam.getStartTime()));
        }
        if (triggerCreateParam.getEndTime() != null && triggerCreateParam.getEndTime() > 0) {
            triggerBuilder.endAt(new Date(triggerCreateParam.getEndTime()));
        }

        ScheduleBuilder scheduleBuilder;
        // 根据不同的类型,设置不同参数,构建对应类型的触发器
        if ("CRON".equalsIgnoreCase(triggerCreateParam.getTriggerType())) {
            if (StringUtils.isEmpty(triggerCreateParam.getCronExpression())) {
                throw new IllegalArgumentException("CRON任务中,CRON表达式不能为空");
            }
            scheduleBuilder = CronScheduleBuilder.cronSchedule(triggerCreateParam.getCronExpression());

        } else if ("SIMPLE".equalsIgnoreCase(triggerCreateParam.getTriggerType())) {
            if (triggerCreateParam.getRepeatInterval() == null) {
                throw new IllegalArgumentException("简单任务中,重复间隔不能为空");
            }
            if (triggerCreateParam.getRepeatCount() == null) {
                throw new IllegalArgumentException("简单任务中,重复次数不能为空");
            }

            int interval = triggerCreateParam.getRepeatInterval();
            int count = triggerCreateParam.getRepeatCount();
            if (count < 1) {
                scheduleBuilder = SimpleScheduleBuilder.repeatSecondlyForever(interval);
            } else {
                scheduleBuilder = SimpleScheduleBuilder.repeatSecondlyForTotalCount(count, interval);
            }
        } else {
            throw new IllegalArgumentException("不支持的触发器类型:" + triggerCreateParam.getTriggerType());
        }
        Trigger trigger = triggerBuilder.withSchedule(scheduleBuilder).build();
        quartzScheduler.scheduleJob(jobDetail, trigger);
    }

从上面的创建方法,我们可以看到有个类名的检查。Quartz的任务实际执行的逻辑代码,都要继承 org.quartz.Job, 实现execute方法,该方法就是定时任务触发时,实际执行的代码。下面是一个简单的定时任务执行类:

@Component
public class DemoJob extends Job {

    private static final Logger logger = LoggerFactory.getLogger(DemoJob.class);

    @Override
    public void safeExecute(JobExecutionContext context) {
    	// 通过该方法,可以获取到任务定义时传入的参数,实际对应 QRTZ_TRIGGERS.JOB_DATA
        context.getJobDetail().getJobDataMap();
        logger.info("-----------------测试Quartz任务--------------------");
        // 设置执行结果
        context.setResult("咕咕咕咕");
    }

}

创建一个简单任务

TriggerCreateParam createParam = new TriggerCreateParam();
createParam.setJobName("TEST");
createParam.setJobGroup("TEST");
createParam.setTriggerGroup("TEST");
createParam.setTriggerName("TEST");
createParam.setDescription("咕咕咕");
createParam.setTriggerType("SIMPLE");
createParam.setRepeatInterval(10);
createParam.setRepeatCount(5);
myQuartzService.createTrigger(createParam);

执行日志

2020-06-04 18:11:42.344  INFO 16452 --- [eduler_Worker-1] com.netease.core.job.example.DemoJob     : -----------------测试Quartz任务--------------------
2020-06-04 18:11:42.344  INFO 16452 --- [eduler_Worker-1] c.n.c.job.listener.JobRunningListener    : Job DemoJobTest.DemoJobTest execution complete at  18:11:42 06/04/2020 and reports: 咕咕咕咕
2020-06-04 18:11:52.336  INFO 16452 --- [eduler_Worker-2] c.n.c.job.listener.JobRunningListener    : Job DemoJobTest.DemoJobTest fired (by trigger DemoJobTest.DemoJobTest_trigger) at:  18:11:52 06/04/2020
2020-06-04 18:11:52.336  INFO 16452 --- [eduler_Worker-2] com.netease.core.job.example.DemoJob     : -----------------测试Quartz任务--------------------
2020-06-04 18:11:52.336  INFO 16452 --- [eduler_Worker-2] c.n.c.job.listener.JobRunningListener    : Job DemoJobTest.DemoJobTest execution complete at  18:11:52 06/04/2020 and reports: 咕咕咕咕
2020-06-04 18:12:02.333  INFO 16452 --- [eduler_Worker-3] c.n.c.job.listener.JobRunningListener    : Job DemoJobTest.DemoJobTest fired (by trigger DemoJobTest.DemoJobTest_trigger) at:  18:12:02 06/04/2020
2020-06-04 18:12:02.333  INFO 16452 --- [eduler_Worker-3] com.netease.core.job.example.DemoJob     : -----------------测试Quartz任务--------------------
2020-06-04 18:12:02.333  INFO 16452 --- [eduler_Worker-3] c.n.c.job.listener.JobRunningListener    : Job DemoJobTest.DemoJobTest execution complete at  18:12:02 06/04/2020 and reports: 咕咕咕咕
2020-06-04 18:12:12.334  INFO 16452 --- [eduler_Worker-4] c.n.c.job.listener.JobRunningListener    : Job DemoJobTest.DemoJobTest fired (by trigger DemoJobTest.DemoJobTest_trigger) at:  18:12:12 06/04/2020
2020-06-04 18:12:12.334  INFO 16452 --- [eduler_Worker-4] com.netease.core.job.example.DemoJob     : -----------------测试Quartz任务--------------------
2020-06-04 18:12:12.334  INFO 16452 --- [eduler_Worker-4] c.n.c.job.listener.JobRunningListener    : Job DemoJobTest.DemoJobTest execution complete at  18:12:12 06/04/2020 and reports: 咕咕咕咕
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章