前言
Quartz 是基于 Java 實作的任務排程架構,對任務的建立、修改、删除、觸發以及監控這些操作直接提供了 api,這意味着開發人員擁有最大的操作權,也帶來了更高的靈活性。
什麼是任務排程?
任務排程指在将來某個特定的時間、固定的時間周期或規律變化的時間周期到達時自動排程并執行指定的任務。
文章稍長,建議點贊收藏😉,點選跳轉目錄👉 SpringBoot 整合 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。
三、體系結構
四、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發行版的
目錄中找到表建立SQL腳本docs/dbTables
🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶🪶
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的監聽器用于當任務排程中你所關注事件發生時,能夠及時擷取這一事件的通知。類似于任務執行過程中的郵件、短信類的提醒
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);
}
方法說明
-
:用于擷取改JobListener 的名稱getName()
-
:Scheduler 在 JobDetail 将要被執行時調用這個方法jobToBeExecuted()
-
:Scheduler 在 JobDetail 即将被執行,但又被 TriggerListener 否決時會調用該方法jobExecutionVetoed()
-
:Scheduler 在 JobDetail 被執行之後調用這個方法jobWasExecuted()
代碼示例
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()
-
:當與監聽器相關聯的Trigger被觸發,Job上的execute()方法将被執行時,Scheduler就調用該方法。triggerFired()
-
:在 Trigger 觸發後,Job 将要被執行時由 Scheduler 調用這個方法。TriggerListener 給了一個選擇去否決 Job 的執行。假如這個方法傳回 true,這個 Job 将不會為此次 Trigger 觸發而得到執行。vetoJobExecution()
-
:Scheduler 調用這個方法是在 Trigger 錯過觸發時。你應該關注此方法中持續時間長的邏輯:在出現許多錯過觸發的 Trigger 時,長邏輯會導緻骨牌效應。你應當保持這上方法盡量的小。triggerMisfired()
-
:Trigger 被觸發并且完成了 Job 的執行時,Scheduler 調用這個方法。triggerComplete()
代碼示例
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();
}
方法說明
-
:用于部署JobDetail時調用jobScheduled()
-
:用于解除安裝JobDetail時調用jobUnscheduled()
-
:當一個 Trigger 來到了再也不會觸發的狀态時調用這個方法。除非這個 Job 已設定成了持久性,否則它就會從 Scheduler 中移除。triggerFinalized()
-
:Scheduler 調用這個方法是發生在一個 Trigger 或 Trigger 組被暫停時。假如是 Trigger 組的話,triggerName 參數将為 null。triggersPaused()
-
:Scheduler 調用這個方法是發生成一個 Trigger 或 Trigger 組從暫停中恢複時。假如是 Trigger 組的話,假如是 Trigger 組的話,triggerName 參數将為 null。參數将為 null。triggersResumed()
-
:當一個或一組 JobDetail 暫停時調用這個方法。jobsPaused()
-
:當一個或一組 Job 從暫停上恢複時調用這個方法。假如是一個 Job 組,jobName 參數将為 null。jobsResumed()
-
:在 Scheduler 的正常運作期間産生一個嚴重錯誤時調用這個方法。schedulerError()
-
:當Scheduler 開啟時,調用該方法schedulerStarted()
-
: 當Scheduler處于StandBy模式時,調用該方法schedulerInStandbyMode()
-
:當Scheduler停止時,調用該方法schedulerShutdown()
-
:當Scheduler中的資料被清除時,調用該方法。schedulingDataCleared()
代碼示例
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的入門案例,實作動态管理定時任務,以上方法親測有效,希望能給大家一個參考。
創作不易,關注💖、點贊👍、收藏🎉就是對作者最大的鼓勵👏,歡迎在下方評論留言🧐