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: 咕咕咕咕
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章