天天看點

Quartz 快速入門案例,看這一篇就夠了前言一、Quartz 是什麼?二、核心概念三、體系結構四、SpringBoot 整合 Quartz五、Quartz 監聽器總結

前言

Quartz 是基于 Java 實作的任務排程架構,對任務的建立、修改、删除、觸發以及監控這些操作直接提供了 api,這意味着開發人員擁有最大的操作權,也帶來了更高的靈活性。

什麼是任務排程?

任務排程指在将來某個特定的時間、固定的時間周期或規律變化的時間周期到達時自動排程并執行指定的任務。

文章稍長,建議點贊收藏😉,點選跳轉目錄👉 SpringBoot 整合 Quartz 👇

Quartz 快速入門案例,看這一篇就夠了前言一、Quartz 是什麼?二、核心概念三、體系結構四、SpringBoot 整合 Quartz五、Quartz 監聽器總結

一、Quartz 是什麼?

Quartz 是一個開源的作業排程架構,它完全由 Java 寫成,并設計用于 J2SE 和 J2EE 應用中。它提供了巨大的靈活性而不犧牲簡單性。你能夠用它來為執行一個作業而建立簡單的或複雜的排程。它有很多特征,如:資料庫支援,叢集,插件,EJB 作業預建構,JavaMail 及其它,支援 cron-like 表達式等等。

官網位址👉 http://www.quartz-scheduler.org

示例代碼:

import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzTest {
    public static void main(String[] args) {
        try {
            // Grab the Scheduler instance from the Factory 
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            // and start it off
            scheduler.start();

            scheduler.shutdown();

        } catch (SchedulerException se) {
            se.printStackTrace();
        }
    }
}
           

在start()和shutdown()之間可以執行一些操作

// define the job and tie it to our HelloJob class
JobDetail job = JobBuilder.newJob(HelloJob.class)
    .withIdentity("job1", "group1")
    .build();

// Trigger the job to run now, and then repeat every 40 seconds
Trigger trigger=TriggerBuilder.newTrigger()
    .withIdentity("trigger1", "group1")
    .startNow()
    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
    .withIntervalInSeconds(40)
    .repeatForever())            
    .build();

// Tell quartz to schedule the job using our trigger
scheduler.scheduleJob(job, trigger);
           

二、核心概念

常用API 概述
Scheduler 與排程程式互動的主要API
Job 你想要排程器執行的任務元件需要實作的接口
JobDetail 用于定義作業的執行個體
Trigger 定義執行給定作業的計劃的元件
JobBuilder 用于定義/建構 JobDetail 執行個體,用于定義作業的執行個體
TriggerBuilder 用于定義/建構觸發器執行個體

1.任務Job

我們想要排程的任務都必須實作

org.quartz.job

接口,然後實作接口中定義的

execute( )

方法

代碼如下(示例):

@Slf4j
public class HelloJob implements Job {

    @Override
    public void execute(JobExecutionContext context) {
        log.info("Hello Job 執行時間: {}", DateUtil.now());
    }
}
           

那麼如何給job執行個體增加屬性或配置呢?如何在job的多次執行中,跟蹤job的狀态呢?

答案就是

JobDataMap

,JobDetail對象的一部分。

JobDataMap 可以包含不限量的(序列化的)資料對象,在 job 執行個體執行的時候,可以使用其中的資料;

JobDataMap 是 Java Map接口的一個實作,額外增加了一些便于存取基本類型的資料的方法。

如下示例:

// define the job and tie it to our DumbJob class
  JobDetail job = newJob(DumbJob.class)
      .withIdentity("myJob", "group1") // name "myJob", group "group1"
      .usingJobData("jobSays", "Hello World!")
      .usingJobData("myFloatValue", 3.141f)
      .build();
           
public class HelloJob implements Job {
    public HelloJob() {
    }

    public void execute(JobExecutionContext context)throws JobExecutionException{
      JobKey key = context.getJobDetail().getKey();
      JobDataMap dataMap = context.getJobDetail().getJobDataMap();
      String jobSays = dataMap.getString("jobSays");
      float myFloatValue = dataMap.getFloat("myFloatValue");
      System.err.println("Instance " + key + " of HelloJob says: " + jobSays + ", and val is: " + myFloatValue);
    }
}
           

Job 狀态

在調用 execute 方法之前都會建立一個新的 Job 執行個體,這就牽引出了 Job 狀态的概念
有無狀态 說明
無狀态的 Job 每次調用時都會建立一個新的 JobDataMap
有狀态的 Job 多次 Job 調用可以持有一些狀态資訊,這些狀态資訊存儲在 JobDataMap 中

跟蹤 Job狀态時,需要在任務類上加個注解

@PersistJobDataAfterExecution

,讓 Job 變成有狀态

@Slf4j
@PersistJobDataAfterExecution
public class HelloJob implements Job {

    private Integer executeCount;

    public void setExecuteCount(Integer executeCount) {
        this.executeCount = executeCount;
    }

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String data = LocalDateTime.now().
            format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        log.info("execute count: {}, current time: {}",
                 ++executeCount, data);
        //将累加的 count 存入JobDataMap中
        jobExecutionContext.getJobDetail().
            getJobDataMap().put("executeCount", executeCount);
    }
}
/** OUTPUT:
execute count: 1, current time: 2020-11-17 22:28:48
execute count: 2, current time: 2020-11-17 22:28:52
execute count: 3, current time: 2020-11-17 22:28:57
**/
           

2.觸發器 Trigger

Trigger 作為執行任務的排程器

。我們如果想要淩晨1點執行備份資料的任務,那麼 Trigger 就會設定淩晨1點執行該任務。其中 Trigger 又分為

SimpleTrigger

CronTrigger

兩種,通過一個 TriggerKey 唯一辨別

公共屬性

屬性 概述
JobKey 表示job執行個體的辨別,觸發器被觸發時,該指定的job執行個體會執行
StartTime 表示觸發器的時間表 首次被觸發的時間,值類型是Java.util.Date
EndTime 指定觸發器的不再觸發的時間,它的值類型是Java.util.Date

優先級(priority)

如果 Trigger 有多個,你可以為 Trigger 設定

priority(優先級)

屬性(priority屬性的值可以是任意整數,正數、負數),優先級高的 Trigger 會被首先觸發,如果沒有為trigger設定優先級,trigger使用預設優先級,值為5;

PS:🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧

  • 隻有同時觸發的trigger之間才會比較優先級。10:59觸發的trigger總是在11:00觸發的trigger之前執行。
  • 如果trigger是可恢複的,在恢複後再排程時,優先級與原trigger是一樣的。

🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧🚧

SimpleTrigger 及 CronTrigger

觸發器 對比
SimpleTrigger 在一個指定時間段内執行一次作業任務或是在指定時間間隔内執行多次作業任務
CronTrigger 基于月曆的作業排程器,Cron表達式配置CronTrigger的執行個體,而不是像SimpleTrigger那樣精确指定間隔時間,比SimpleTrigger更常用

SimpleTrigger

SimpleTrigger 對于設定和使用是最為簡單的一種 QuartzTrigger,它是為那種需要在特定的日期/時間啟動,且以一個可能的間隔時間重複執行 n 次的 Job任務 所設計的。

比如我想要在一個指定的時間段内執行一次任務,我們隻需要這樣寫:

Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("testTrigger", "testTriggerGroup")
    .startAt(startTime) //自定義執行時間
    .build();
           

再者我想在指定的時間間隔内多次執行該任務,我們可以這樣寫:

Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("testTrigger", "testTriggerGroup")
    .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
    .withRepeatCount(2)) // 每5秒執行一次,連續執行3次後停止,從 0 開始計數
    .build();
           

我們來總結一下上面的示例:

  • SimpleTrigger具備的屬性有:

    開始時間

    結束時間

    重複次數

    重複的時間間隔

  • 重複次數

    的值可以為 、

    正整數

    、或

    常量 SimpleTrigger.REPEAT_INDEFINITELY

  • 重複的時間間隔

    屬性值必須大于 或長整型的正整數,以 毫秒 作為時間機關,當

    重複的時間間隔為 0

    時,意味着與

    Trigger

    同時觸發執行
  • 結束時間和重複次數同時存在時,以結束時間優先

CronTrigger

跟 SimpleTrigger 執行間隔時間觸發的相比,CronTrigger 更加靈活,它是

基于月曆的作業排程器

。使用 CronTrigger 我們可以執行某個時間點執行,例如 “每天的淩晨1點執行”、“每個工作日的 12 點執行”,也可以像 SimpleTrigger 那樣執行一個開始時間和結束時間運作任務

Cron表達式 是用來配置 CronTrigger 執行個體,它是一個由 7 個子表達式組成的字元串(線上Cron表達式生成器)

使用示例

Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("testTrigger", "testTriggerGroup")
    .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * 6 4 ?"))
    .build();
           

3.執行個體 JobDetail

Quartz 在每次執行 Job 時,都重新建立一個 Job 執行個體,是以它不直接接受一個 Job 的執行個體,相反它接收一個 Job 實作類。描述 Job 的實作類及其它相關的靜态資訊,如 Job 名字、描述等。

JobDetail 為 Job 執行個體提供了許多設定屬性(name、group、jobClasS、jobDataMap),以及

JobDetaMap 成員變量屬性

,它用來存儲特定Job執行個體的狀态資訊,排程器需要借助 JobDetail 對象來添加 Job 執行個體。

4.排程器 Scheduler

Scheduler為任務的排程器

,它會将任務 job 及觸發器 Trigger 整合起來,負責基于 Trigger 設定的時間來執行 Job。

三、體系結構

Quartz 快速入門案例,看這一篇就夠了前言一、Quartz 是什麼?二、核心概念三、體系結構四、SpringBoot 整合 Quartz五、Quartz 監聽器總結

四、SpringBoot 整合 Quartz

上面我們大概介紹了 Quartz,那麼該如何使用了,請往下看:

1.引入依賴

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

2.Quartz 配置

quartz:
    # 參見 org.springframework.boot.autoconfigure.quartz.QuartzProperties
    job-store-type: jdbc
    wait-for-jobs-to-complete-on-shutdown: true
    scheduler-name: SpringBootDemoScheduler
    properties:
      org.quartz.threadPool.threadCount: 5
      org.quartz.threadPool.threadPriority: 5
      org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
      org.quartz.jobStore.misfireThreshold: 5000
      org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
      org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
      # 在排程流程的第一步,也就是拉取待即将觸發的triggers時,是上鎖的狀态,即不會同時存在多個線程拉取到相同的trigger的情況,也就避免的重複排程的危險。
      org.quartz.jobStore.acquireTriggersWithinLock: true
           
工作資料 概述
RAMJobStore 将其所有資料儲存在RAM中,是使用最簡單的JobStore,它也是性能最高的(在CPU時間方面
JDBCJobStore 通過JDBC将其所有資料儲存在資料庫中

說明: 🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶

  • JDBCJobStore幾乎與任何資料庫一起使用,已被廣泛應用于Oracle,PostgreSQL,MySQL,MSSQLServer,HSQLDB和DB2。
  • 要使用JDBCJobStore,必須首先建立一組資料庫表以供Quartz使用。
  • 可以在Quartz發行版的

    docs/dbTables

    目錄中找到表建立SQL腳本

🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶

Quartz 快速入門案例,看這一篇就夠了前言一、Quartz 是什麼?二、核心概念三、體系結構四、SpringBoot 整合 Quartz五、Quartz 監聽器總結

3.自定義任務

HelloJob.java

@Slf4j
public class HelloJob implements Job {

    @Override
    public void execute(JobExecutionContext context) {
        log.info("Hello Job 執行時間: {}", DateUtil.now());
    }
}
           

TestJob.java

@Slf4j
public class TestJob implements Job {

    @Override
    public void execute(JobExecutionContext context) {
        log.info("Hello Job 執行時間: {}", DateUtil.now());
    }
}
           

4.實作動态管理定時任務

對任務的

暫停

恢複

删除

等操作,隻需調用Scheduler 的對應方法即可

JobKey jobKey = JobKey.jobKey(“jobName”, “jobGroup”);

scheduler.pauseJob(jobKey);//暫停任務

scheduler.resumeJob(jobKey);//恢複任務

scheduler.deleteJob(jobKey);//删除任務

封裝任務排程類

JobForm.java

@Data
public class JobForm {
    /**
     * 定時任務全類名
     */
    @NotBlank(message = "類名不能為空")
    private String jobClassName;
    /**
     * 任務組名
     */
    @NotBlank(message = "任務組名不能為空")
    private String jobGroupName;
    /**
     * 定時任務cron表達式
     */
    @NotBlank(message = "cron表達式不能為空")
    private String cronExpression;
}
           

JobService.java

public interface JobService {
   /**
     * 添加并啟動定時任務
     *
     * @param form 表單參數 
     * @throws Exception 異常
     */
    void addJob(JobForm form) throws Exception;

    /**
     * 删除定時任務
     *
     * @param form 表單參數 
     * @throws SchedulerException 異常
     */
    void deleteJob(JobForm form) throws SchedulerException;

    /**
     * 暫停定時任務
     *
     * @param form 表單參數 
     * @throws SchedulerException 異常
     */
    void pauseJob(JobForm form) throws SchedulerException;

    /**
     * 恢複定時任務
     *
     * @param form 表單參數 
     * @throws SchedulerException 異常
     */
    void resumeJob(JobForm form) throws SchedulerException;

    /**
     * 重新配置定時任務
     *
     * @param form 表單參數 
     * @throws Exception 異常
     */
    void cronJob(JobForm form) throws Exception;

    /**
     * 查詢定時任務清單
     *
     * @param currentPage 目前頁
     * @param pageSize    每頁條數
     * @return 定時任務清單
     */
    PageInfo<JobAndTrigger> list(Integer currentPage, Integer pageSize);
}
           

JobServiceImpl.java

@Service
@Slf4j
public class JobServiceImpl implements JobService {
    private final Scheduler scheduler;
    private final JobMapper jobMapper;

    @Autowired
    public JobServiceImpl(Scheduler scheduler, JobMapper jobMapper) {
        this.scheduler = scheduler;
        this.jobMapper = jobMapper;
    }

    /**
     * 添加并啟動定時任務
     *{@link JobForm}
     * @return 
     * @throws Exception 異常
     */
    @Override
    public void addJob(JobForm form) throws Exception {
        // 啟動排程器
        scheduler.start();

        // 建構Job資訊
        JobDetail jobDetail = JobBuilder
            .newJob(JobUtil.getClass(form.getJobClassName()).getClass())
            .withIdentity(form.getJobClassName(), form.getJobGroupName())
            .build();

        // Cron表達式排程建構器(即任務執行的時間)
        CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(form.getCronExpression());

        //根據Cron表達式建構一個Trigger
        CronTrigger trigger = TriggerBuilder.newTrigger()
            .withIdentity(form.getJobClassName(), form.getJobGroupName())
            .withSchedule(cron)
            .build();

        try {
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) {
            log.error("【定時任務】建立失敗!", e);
            throw new Exception("【定時任務】建立失敗!");
        }

    }

    /**
     * 删除定時任務
     *{@link JobForm}
     * @throws SchedulerException 異常
     */
    @Override
    public void deleteJob(JobForm form) throws SchedulerException {
        scheduler.pauseTrigger(TriggerKey.triggerKey(form.getJobClassName(), form.getJobGroupName()));
        scheduler.unscheduleJob(TriggerKey.triggerKey(form.getJobClassName(), form.getJobGroupName()));
        scheduler.deleteJob(JobKey.jobKey(form.getJobClassName(), form.getJobGroupName()));
    }

    /**
     * 暫停定時任務
     *{@link JobForm}
     * @throws SchedulerException 異常
     */
    @Override
    public void pauseJob(JobForm form) throws SchedulerException {
        scheduler.pauseJob(JobKey.jobKey(form.getJobClassName(), form.getJobGroupName()));
    }

    /**
     * 恢複定時任務
     *{@link JobForm}
     * @throws SchedulerException 異常
     */
    @Override
    public void resumeJob(JobForm form) throws SchedulerException {
        scheduler.resumeJob(JobKey.jobKey(form.getJobClassName(), form.getJobGroupName()));
    }

    /**
     * 重新配置定時任務
     *{@link JobForm}
     * @throws Exception 異常
     */
    @Override
    public void cronJob(JobForm form) throws Exception {
        try {
            TriggerKey triggerKey = TriggerKey
                .triggerKey(form.getJobClassName(), form.getJobGroupName());
            // 表達式排程建構器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
                .cronSchedule(form.getCronExpression());

            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            // 根據Cron表達式建構一個Trigger
            trigger = trigger.getTriggerBuilder()
                .withIdentity(triggerKey)
                .withSchedule(scheduleBuilder)
                .build();

            // 按新的trigger重新設定job執行
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {
            log.error("【定時任務】更新失敗!", e);
            throw new Exception("【定時任務】建立失敗!");
        }
    }

    /**
     * 查詢定時任務清單
     *
     * @param currentPage 目前頁
     * @param pageSize    每頁條數
     * @return 定時任務清單
     */
    @Override
    public PageInfo<JobAndTrigger> list(Integer currentPage, Integer pageSize) {
        PageHelper.startPage(currentPage, pageSize);
        List<JobAndTrigger> list = jobMapper.list();
        return new PageInfo<>(list);
    }
}
           

任務排程接口

JobController.java

@RestController
@RequestMapping("/job")
@Slf4j
public class JobController {
    private final JobService jobService;

    @Autowired
    public JobController(JobService jobService) {
        this.jobService = jobService;
    }

    /**
     * 儲存定時任務
     */
    @PostMapping
    public ResponseEntity<ApiResponse> addJob(@Valid JobForm form) {
        try {
            jobService.addJob(form);
        } catch (Exception e) {
            return new ResponseEntity<>(ApiResponse.msg(e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
        }

        return new ResponseEntity<>(ApiResponse.msg("操作成功"), HttpStatus.CREATED);
    }

    /**
     * 删除定時任務
     */
    @DeleteMapping
    public ResponseEntity<ApiResponse> deleteJob(JobForm form) throws SchedulerException {
        if (StrUtil.hasBlank(form.getJobGroupName(), form.getJobClassName())) {
            return new ResponseEntity<>(ApiResponse.msg("參數不能為空"), HttpStatus.BAD_REQUEST);
        }

        jobService.deleteJob(form);
        return new ResponseEntity<>(ApiResponse.msg("删除成功"), HttpStatus.OK);
    }

    /**
     * 暫停定時任務
     */
    @PutMapping(params = "pause")
    public ResponseEntity<ApiResponse> pauseJob(JobForm form) throws SchedulerException {
        if (StrUtil.hasBlank(form.getJobGroupName(), form.getJobClassName())) {
            return new ResponseEntity<>(ApiResponse.msg("參數不能為空"), HttpStatus.BAD_REQUEST);
        }

        jobService.pauseJob(form);
        return new ResponseEntity<>(ApiResponse.msg("暫停成功"), HttpStatus.OK);
    }

    /**
     * 恢複定時任務
     */
    @PutMapping(params = "resume")
    public ResponseEntity<ApiResponse> resumeJob(JobForm form) throws SchedulerException {
        if (StrUtil.hasBlank(form.getJobGroupName(), form.getJobClassName())) {
            return new ResponseEntity<>(ApiResponse.msg("參數不能為空"), HttpStatus.BAD_REQUEST);
        }

        jobService.resumeJob(form);
        return new ResponseEntity<>(ApiResponse.msg("恢複成功"), HttpStatus.OK);
    }

    /**
     * 修改定時任務,定時時間
     */
    @PutMapping(params = "cron")
    public ResponseEntity<ApiResponse> cronJob(@Valid JobForm form) {
        try {
            jobService.cronJob(form);
        } catch (Exception e) {
            return new ResponseEntity<>(ApiResponse.msg(e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
        }

        return new ResponseEntity<>(ApiResponse.msg("修改成功"), HttpStatus.OK);
    }

    @GetMapping
    public ResponseEntity<ApiResponse> jobList(Integer currentPage, Integer pageSize) {
        if (ObjectUtil.isNull(currentPage)) {
            currentPage = 1;
        }
        if (ObjectUtil.isNull(pageSize)) {
            pageSize = 10;
        }
        PageInfo<JobAndTrigger> all = jobService.list(currentPage, pageSize);
        return ResponseEntity.ok(ApiResponse.ok(Dict.create().set("total", all.getTotal()).set("data", all.getList())));
    }

}
           

示範示例

啟動項目,輸入http://localhost:8080/demo/job.html,進入管理界面,如圖所示:

Quartz 快速入門案例,看這一篇就夠了前言一、Quartz 是什麼?二、核心概念三、體系結構四、SpringBoot 整合 Quartz五、Quartz 監聽器總結

點選添加按鈕,添加測試任務(每秒執行一次)

Quartz 快速入門案例,看這一篇就夠了前言一、Quartz 是什麼?二、核心概念三、體系結構四、SpringBoot 整合 Quartz五、Quartz 監聽器總結
Quartz 快速入門案例,看這一篇就夠了前言一、Quartz 是什麼?二、核心概念三、體系結構四、SpringBoot 整合 Quartz五、Quartz 監聽器總結

添加完成後,檢視背景日志,成功執行相關任務,其他操作類似,這裡就不一一列舉了

Quartz 快速入門案例,看這一篇就夠了前言一、Quartz 是什麼?二、核心概念三、體系結構四、SpringBoot 整合 Quartz五、Quartz 監聽器總結

五、Quartz 監聽器

Quartz的監聽器用于當任務排程中你所關注事件發生時,能夠及時擷取這一事件的通知。類似于任務執行過程中的郵件、短信類的提醒

Quartz監聽器主要有

JobListener

TriggerListener

SchedulerListener

三種,分别表示任務、觸發器、排程器對應的監聽器

1.JobListener

任務排程中,與任務 Job 相關的事件包括: Job 開始要執行的提示,執行完成的提示,接口如下:

public interface JobListener {
  	String getName();
  	void jobToBeExecuted(JobExecutionContext context);
  	void jobExecutionVetoed(JobExecutionContext context);
    void jobWasExecuted(JobExecutionContext context,
    JobExecutionException jobException);
}
           

方法說明

  • getName()

    :用于擷取改JobListener 的名稱
  • jobToBeExecuted()

    :Scheduler 在 JobDetail 将要被執行時調用這個方法
  • jobExecutionVetoed()

    :Scheduler 在 JobDetail 即将被執行,但又被 TriggerListener 否決時會調用該方法
  • jobWasExecuted()

    :Scheduler 在 JobDetail 被執行之後調用這個方法

代碼示例

Job 任務類

@Slf4j
@PersistJobDataAfterExecution
public class TestJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext){
        System.out.println("TestJob 執行啦");
    }
}
           

JobListener

public class MyJobListener implements JobListener {
    @Override
    public String getName() {
        String name = getClass().getSimpleName();
        System.out.println("監聽器的名稱是:" + name);
        return name;
    }

    @Override
    public void jobToBeExecuted(JobExecutionContext context) {
        String jobName = context.getJobDetail().getKey().getName();
        System.out.println("Job的名稱是:" + jobName + "\tScheduler在JobDetail将要被執行時調用這個方法");
    }

    @Override
    public void jobExecutionVetoed(JobExecutionContext context) {
        String jobName = context.getJobDetail().getKey().getName();
        System.out.println("Job的名稱是:" + jobName + "\tScheduler在JobDetail即将被執行,但又被TriggerListerner否決時會調用該方法");
    }

    @Override
    public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
        String jobName = context.getJobDetail().getKey().getName();
        System.out.println("Job的名稱是:" + jobName + "\tScheduler在JobDetail被執行之後調用這個方法");
    }
}
           

任務排程類

@Slf4j
public class TestScheduler {
    public static void main(String[] args) throws Exception {
        // 擷取任務排程的執行個體
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 定義任務排程執行個體, 并與TestJob綁定
        JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
            .usingJobData("executeCount", 0)
            .withIdentity("testJob", "testJobGroup")
            .build();
        // 定義觸發器, 會馬上執行一次, 接着5秒執行一次
        Trigger trigger = TriggerBuilder.newTrigger()
            .usingJobData("testInfo", "trigger資料存放")
            .withIdentity("testTrigger", "testTriggerGroup")
            .startNow()
            .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
            .build();
        // 建立并注冊一個全局的Job Listener
        scheduler.getListenerManager()
            .addJobListener(new MyJobListener(),
                            EverythingMatcher.allJobs());

        // 使用觸發器排程任務的執行
        scheduler.scheduleJob(jobDetail, trigger);
        // 開啟任務
        scheduler.start();
    }
}
/** OUTPUT:
監聽器的名稱是:MyJobListener
Job的名稱是:testJob Scheduler在JobDetail将要被執行時調用這個方法
TestJob 執行啦
監聽器的名稱是:MyJobListener
Job的名稱是:testJob Scheduler在JobDetail被執行之後調用這個方法
**/
           

2. TriggerListener

任務排程中,與觸發器 Trigger 相關的事件包括: 觸發器觸發、觸發器未正常觸發、觸發器完成等

public interface TriggerListener {
    public String getName();
    public void triggerFired(Trigger trigger, JobExecutionContext context);
    public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context);
    public void triggerMisfired(Trigger trigger);
    public void triggerComplete(Trigger trigger, JobExecutionContext context, int triggerInstructionCode);
}
           

方法說明

  • getName()

    :用于擷取觸發器的名稱
  • triggerFired()

    :當與監聽器相關聯的Trigger被觸發,Job上的execute()方法将被執行時,Scheduler就調用該方法。
  • vetoJobExecution()

    :在 Trigger 觸發後,Job 将要被執行時由 Scheduler 調用這個方法。TriggerListener 給了一個選擇去否決 Job 的執行。假如這個方法傳回 true,這個 Job 将不會為此次 Trigger 觸發而得到執行。
  • triggerMisfired()

    :Scheduler 調用這個方法是在 Trigger 錯過觸發時。你應該關注此方法中持續時間長的邏輯:在出現許多錯過觸發的 Trigger 時,長邏輯會導緻骨牌效應。你應當保持這上方法盡量的小。
  • triggerComplete()

    :Trigger 被觸發并且完成了 Job 的執行時,Scheduler 調用這個方法。

代碼示例

Job 任務類

@Slf4j
@PersistJobDataAfterExecution
public class TestJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext){
        System.out.println("TestJob 執行啦");
    }
}
           

TriggerListener

public class MyTriggerListener implements TriggerListener {
    private String name;

    public MyTriggerListener(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void triggerFired(Trigger trigger, JobExecutionContext context) {
        String triggerName = trigger.getKey().getName();
        System.out.println(triggerName + " 被觸發");
    }

    @Override
    public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
        String triggerName = trigger.getKey().getName();
        System.out.println(triggerName + " 沒有被觸發");
        return false; // true:表示不會執行Job的方法
    }

    @Override
    public void triggerMisfired(Trigger trigger) {
        String triggerName = trigger.getKey().getName();
        System.out.println(triggerName + " 錯過觸發");
    }

    @Override
    public void triggerComplete(Trigger trigger, JobExecutionContext jobExecutionContext, Trigger.CompletedExecutionInstruction completedExecutionInstruction) {
        String triggerName = trigger.getKey().getName();
        System.out.println(triggerName + " 完成之後觸發");
    }

}
           

任務排程類

@Slf4j
public class TestScheduler {
    public static void main(String[] args) throws Exception {
        // 擷取任務排程的執行個體
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 定義任務排程執行個體, 并與TestJob綁定
        JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
            .usingJobData("executeCount", 0)
            .withIdentity("testJob", "testJobGroup")
            .build();
        // 定義觸發器, 會馬上執行一次, 接着5秒執行一次
        Trigger trigger = TriggerBuilder.newTrigger()
            .usingJobData("testInfo", "trigger資料存放")
            .withIdentity("testTrigger", "testTriggerGroup")
            .startNow()
            .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
            .build();

        // 建立并注冊一個全局的Trigger Listener
        scheduler.getListenerManager().addTriggerListener(new MyTriggerListener("simpleTrigger"), EverythingMatcher.allTriggers());

        // 使用觸發器排程任務的執行
        scheduler.scheduleJob(jobDetail, trigger);
        // 開啟任務
        scheduler.start();
    }
}
/** OUTPUT:
testTrigger 被觸發
testTrigger 沒有被觸發
TestJob 執行啦
testTrigger 完成之後觸發
**/
           

2. SchedulerListener

SchedulerListener 會在Scheduler的生命周期中關鍵事件發生時被調用。與Scheduler有關的事件包括:

增加一個job/trigger

删除一個job/trigger

scheduler發生嚴重錯誤

關閉scheduler

等。

public interface SchedulerListener {

    public void jobScheduled(Trigger trigger);
    public void jobUnscheduled(String triggerName, String triggerGroup);
    public void triggerFinalized(Trigger trigger);
    public void triggersPaused(String triggerName, String triggerGroup);
    public void triggersResumed(String triggerName, String triggerGroup);
    public void jobsPaused(String jobName, String jobGroup);
    public void jobsResumed(String jobName, String jobGroup);
    public void schedulerError(String msg, SchedulerException cause);
    public void schedulerStarted();
    public void schedulerInStandbyMode();
    public void schedulerShutdown();
    public void schedulingDataCleared();
}
           

方法說明

  • jobScheduled()

    :用于部署JobDetail時調用
  • jobUnscheduled()

    :用于解除安裝JobDetail時調用
  • triggerFinalized()

    :當一個 Trigger 來到了再也不會觸發的狀态時調用這個方法。除非這個 Job 已設定成了持久性,否則它就會從 Scheduler 中移除。
  • triggersPaused()

    :Scheduler 調用這個方法是發生在一個 Trigger 或 Trigger 組被暫停時。假如是 Trigger 組的話,triggerName 參數将為 null。
  • triggersResumed()

    :Scheduler 調用這個方法是發生成一個 Trigger 或 Trigger 組從暫停中恢複時。假如是 Trigger 組的話,假如是 Trigger 組的話,triggerName 參數将為 null。參數将為 null。
  • jobsPaused()

    :當一個或一組 JobDetail 暫停時調用這個方法。
  • jobsResumed()

    :當一個或一組 Job 從暫停上恢複時調用這個方法。假如是一個 Job 組,jobName 參數将為 null。
  • schedulerError()

    :在 Scheduler 的正常運作期間産生一個嚴重錯誤時調用這個方法。
  • schedulerStarted()

    :當Scheduler 開啟時,調用該方法
  • schedulerInStandbyMode()

    : 當Scheduler處于StandBy模式時,調用該方法
  • schedulerShutdown()

    :當Scheduler停止時,調用該方法
  • schedulingDataCleared()

    :當Scheduler中的資料被清除時,調用該方法。

代碼示例

Job 任務類

@Slf4j
@PersistJobDataAfterExecution
public class TestJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext){
        System.out.println("TestJob 執行啦");
    }
}
           

SchedulerListener

public class MySchedulerListener implements SchedulerListener {
    @Override
    public void jobScheduled(Trigger trigger) {
        String jobName = trigger.getJobKey().getName();
        System.out.println(jobName + " 完成部署");
    }

    @Override
    public void jobUnscheduled(TriggerKey triggerKey) {
        System.out.println(triggerKey + " 完成解除安裝");
    }

    @Override
    public void triggerFinalized(Trigger trigger) {
        System.out.println("觸發器被移除 " + trigger.getJobKey().getName());
    }

    @Override
    public void triggerPaused(TriggerKey triggerKey) {
        System.out.println(triggerKey + " 正在被暫停");
    }

    @Override
    public void triggersPaused(String triggerGroup) {
        System.out.println("觸發器組 " + triggerGroup + " 正在被暫停");
    }

    @Override
    public void triggerResumed(TriggerKey triggerKey) {
        System.out.println(triggerKey + " 正在從暫停中恢複");
    }

    @Override
    public void triggersResumed(String triggerGroup) {
        System.out.println("觸發器組 " + triggerGroup + " 正在從暫停中恢複");
    }

    @Override
    public void jobAdded(JobDetail jobDetail) {
        System.out.println(jobDetail.getKey() + " 添加工作任務");
    }

    @Override
    public void jobDeleted(JobKey jobKey) {
        System.out.println(jobKey + " 删除工作任務");
    }

    @Override
    public void jobPaused(JobKey jobKey) {
        System.out.println(jobKey + " 工作任務正在被暫停");
    }

    @Override
    public void jobsPaused(String jobGroup) {
        System.out.println("工作任務組 " + jobGroup + " 正在被暫停");
    }

    @Override
    public void jobResumed(JobKey jobKey) {
        System.out.println(jobKey + " 正在從暫停中恢複");
    }

    @Override
    public void jobsResumed(String jobGroup) {
        System.out.println("工作任務組 " + jobGroup + " 正在從暫停中恢複");
    }

    @Override
    public void schedulerError(String msg, SchedulerException cause) {
        System.out.println("産生嚴重錯誤時調用:   " + msg + "  " + cause.getUnderlyingException());
    }

    @Override
    public void schedulerInStandbyMode() {
        System.out.println("排程器在挂起模式下調用");
    }

    @Override
    public void schedulerStarted() {
        System.out.println("排程器 開啟時調用");
    }

    @Override
    public void schedulerStarting() {
        System.out.println("排程器 正在開啟時調用");
    }

    @Override
    public void schedulerShutdown() {
        System.out.println("排程器 已經被關閉 時調用");
    }

    @Override
    public void schedulerShuttingdown() {
        System.out.println("排程器 正在被關閉 時調用");
    }

    @Override
    public void schedulingDataCleared() {
        System.out.println("排程器的資料被清除時調用");
    }
}
           

任務排程類

@Slf4j
public class TestScheduler {
    public static void main(String[] args) throws Exception {
        // 擷取任務排程的執行個體
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 定義任務排程執行個體, 并與TestJob綁定
        JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
                .usingJobData("executeCount", 0)
                .withIdentity("testJob", "testJobGroup")
                .build();
        // 定義觸發器, 會馬上執行一次, 接着5秒執行一次
        Date endTime = new Date();
        endTime.setTime(endTime.getTime()+5000);
        Trigger trigger = TriggerBuilder.newTrigger()
                .usingJobData("testInfo", "trigger資料存放")
                .withIdentity("testTrigger", "testTriggerGroup")
                .startNow()
                .endAt(endTime) //設定了停止時間
                .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
                .build();

        // 建立SchedulerListener
        scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener());

        // 使用觸發器排程任務的執行
        scheduler.scheduleJob(jobDetail, trigger);
        // 開啟任務
        scheduler.start();
    }
}
/** OUTPUT:
testJobGroup.testJob 添加工作任務
testJob 完成部署
排程器 正在開啟時調用
排程器 開啟時調用
TestJob 執行啦
觸發器被移除 testJob
testJobGroup.testJob 删除工作任務
**/
           

總結

以上就是今天要講的内容,本文僅僅簡單介紹了quartz的入門案例,實作動态管理定時任務,以上方法親測有效,希望能給大家一個參考。

創作不易,關注💖、點贊👍、收藏🎉就是對作者最大的鼓勵👏,歡迎在下方評論留言🧐