一、Quartz基本介绍
Quatz 是一个特性丰富的,开源的任务调度库,它几乎可以嵌入所有的 Java 程序,从
很小的独立应用程序到大型商业系统。Quartz 可以用来创建成百上千的简单的或者复杂
的任务,这些任务可以用来执行任何程序可以做的事情。Quartz 拥有很多企业级的特性,
包括支持 JTA 事务和集群。
二、Quartz基本使用
1、导入依赖
Quartz使用前需要引入Quartz的依赖,如下所示:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
2、Quartz 默认配置文件
在Quartz依赖中的 org.quarzt 包路径下有个配置文件 quartz.properties,该配置问价
是 Quartz 的默认配置文件,若在自己的项目中创建了同名的配置文件,即在自己的项目
中存在名称是 quartz.properties 的配置文件,则自定义的 quartz.properties 将覆盖 org.quarzt
包路径的配置文件quartz.properties,
默认配置文件如下所示:
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
org.quartz.jobStore.misfireThreshold: 60000
//配置保存Quartz 运行数据的方式,RAMJobStore 表示Quartz 运行数据保存在内存中,
//保存在内存中的数据当项目重启时数据会丢失
//为了Quartz 运行数据持有化,可以将Quartz 运行数据保存到磁盘中,如保存在数据库中
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
/**
JobStore持久化配置:将Quartz的数据保存到数据库中的配置
*/
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 使用quartz.properties,不使用默认配置
org.quartz.jobStore.useProperties:true
#数据库中quartz表的表名前缀
org.quartz.jobStore.tablePrefix:QRTZ_
org.quartz.jobStore.dataSource:myDS
#配置数据源
org.quartz.dataSource.myDS.driver:com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL:jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf8
org.quartz.dataSource.myDS.user:root
org.quartz.dataSource.myDS.password:123456
org.quartz.dataSource.myDS.validationQuery=select 0 from dual
3、创建Job
Job是Quartz执行的具体个体对象;Job创建也很简单,只需要让自己定义的类实现Quartz
中的Job接口,并实现接口方法execute();
execute() 方法中执行自己需求中需要调度执行的具体业务逻辑,即具体的任务内容;
示例代码如下:
/*******************************************************
* 创建Job,job需要实现 quartz 中的job接口,并实现execute() 方法;
* execute() 方法中执行自己需求中需要调度执行的具体业务逻辑,即具体的任务内容
*
* @author lbf
*
*******************************************************/
public class QuartzJob01 implements Job {
/**
*
* @param context Quartz执行任务job时的上下文对象,通过该上下文对象可以获取JobDetail 设置的数据
* @throws JobExecutionException
*/
public void execute(JobExecutionContext context) throws JobExecutionException {
Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//获取 JobDetail 包装Job时设置的数据集合 JobDataMap
//JobDataMap 通过map 的k-v 的方式保存数据
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
System.out.println( " " + sf.format(date) + " 任务1执行了," + dataMap.getString("msb"));
}
}
注意:
在Quartz 中Job并不能直接执行,需要把Job包装成JobDetail 后,才能被执行;
JobDetail中有个JobDataMap 类型的属性,可以把它看作一个一个Map,其以
key-value的形式保存JobDetail 动态设置的属性数据。
4、创建Trigger
触发器 Trigger,用于定义任务Job出发执行的规律
5、创建Scheduler
调度器 Scheduler 主要用于将 JobDetail 和 Trigger绑定,并注册到容器中。
JobDetail 和 Trigger绑定是“一对多”的关系(1:N),即一个JobDetail可以去绑定多
个触发器Trigger;
Scheduler 先启动后启动无所谓,只要有 Trigger 到达触发条件,就会执行任务。
Scheduler 是单例的
步骤4、5示例代码如下:
/*******************************************************
* 执行任务
*
* @author lbf
*
*******************************************************/
public class MyScheduler {
public static void main(String[] args) throws SchedulerException {
/**
* 将任务 QuartzJob01 包装成一个JobDetail
* withIdentity: 用于设置JobDetail 的唯一标识
* usingJobData:用于给 JobDetail 设置参数数据,该数据以k-v的形式保存在 JobDataMap中,用于扩展调度任务执行时的属性
* ,该数据可以Job的execute 方法中通过上下文对象 JobExecutionContext 获取
*
*/
JobDetail jobDetail = JobBuilder.newJob(QuartzJob01.class)
.withIdentity("job02","grop01")
.usingJobData("msb","haha-java")
.usingJobData("intType",123)
.build();
//创建触发器
//基于 SimpleTrigger 定义了一个每 2 秒钟运行一次、不断重复的 Trigger:
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger01","group01")//设置Trigger 的唯一标识
.startNow() //设置Trigger 立即启动
.withSchedule(
SimpleScheduleBuilder.simpleSchedule() //
.withIntervalInSeconds(2) //设置任务每隔2s执行一次
.repeatForever()
)
.build();
/**
* 创建调度器
*/
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
/**
* 通过调度器绑定 JobDetail和触发器 Trigger,绑定关系是:1:N,即一个JobDetail可以去绑定多个触发器Trigger
*
*/
scheduler.scheduleJob(jobDetail,trigger);
//启动调度器
//Scheduler 先启动后启动无所谓,只要有 Trigger 到达触发条件,就会执行任务。
scheduler.start();
}
}
6、Listener
Quartz 中提供了3种监听器,即 JobListener、TriggerListener、SchedulerListener分
别用来监听 Job、Trigger 和 Scheduler 的运行情况;下边来分别看下这3种监听器的
使用。
6.1、JobListener
JobListener 主要用于监听 JobDetail(即Job) 在运行过程中是否有关键事件发生;
这些关键事件包括:JobDetail是否被Trigger否决、JobDetail 是否执行完成等。
JobListener示例代码如下:
/*******************************************************
* 创建Job监听器,用于监听Job 任务的执行情况,
* Job监听器需要实现接口 JobListener 就可以了
* @author lbf
* @date 2024-12-01 11:07
*******************************************************/
public class MyJobListener implements JobListener {
/**
* 返回监听器的名称
* @return
*/
public String getName() {
String name = getClass().getSimpleName();
System.out.println( "【JobListener】 :"+ "获取到监听器名称:"+name);
return name;
}
/**
* 调度器Scheduler 在即将执行 JobDetail 时执行这个方法
*
* @param jobExecutionContext Job执行时的上下文对象
*/
public void jobToBeExecuted(JobExecutionContext context) {
//获取任务名称
String jobName = context.getJobDetail().getKey().getName();
System.out.println("【JobListener】 :"+ jobName + " ——任务即将执行 ");
}
/**
* 调度器 Scheduler 在 JobDetail 即将被执行,但又被 TriggerListener 否决了时调用这个方法
*
* @param jobExecutionContext
*/
public void jobExecutionVetoed(JobExecutionContext context) {
//获取任务名称
String jobName = context.getJobDetail().getKey().getName();
System.out.println("【JobListener】 :"+ jobName + " ——任务被TriggerListener否决 ");
}
/**
* 调度器 Scheduler 在 JobDetail 被执行之后调用这个方法
* @param jobExecutionContext
* @param e
*/
public void jobWasExecuted(JobExecutionContext context, JobExecutionException e) {
//获取任务名称
String jobName = context.getJobDetail().getKey().getName();
System.out.println("【JobListener】 :"+ jobName + " ——任务执行完成 ");
}
}
/*******************************************************
* MyJobListener测试
* @author lbf
* @date 2024-12-01 11:19
*******************************************************/
public class MyJobListenerTest {
public static void main(String[] args) throws SchedulerException {
// JobDetail
JobDetail jobDetail = JobBuilder.newJob(QuartzJob01.class).withIdentity("job1", "group1").build();
// Trigger
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();
// SchedulerFactory
SchedulerFactory factory = new StdSchedulerFactory();
// Scheduler
Scheduler scheduler = factory.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
//注册一个全局的Job Listener
//监听器由 ListenerManager 管理
//todo 注意:监听器要在最后边添加
scheduler.getListenerManager().addJobListener(new MyJobListener(), EverythingMatcher.allJobs());
scheduler.start();
}
}
6.2、TriggerListener
TriggerListener 主要被用来监听触发器Trigger是否被触发,及触发后Job的execute 方法
是否被执行
TriggerListener 示例代码如下:
/*******************************************************
* 触发器监听器
* @author lbf
* @date 2024-12-01 16:40
*******************************************************/
public class MyTriggerListener implements TriggerListener {
private String name;
public MyTriggerListener(String name) {
this.name = name;
}
public String getName() {
return name;
}
// Trigger 被触发,Job 上的 execute() 方法将要被执行时
public void triggerFired(Trigger trigger, JobExecutionContext context) {
String triggerName = trigger.getKey().getName();
System.out.println("Method 11111 " + triggerName + " was fired");
}
// 在 Trigger 触发后,Job 将要被执行时由 Scheduler 调用这个方法
// 返回true时,这个任务不会被触发
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
String triggerName = trigger.getKey().getName();
System.out.println("Method 222222 " + triggerName + " was not vetoed");
return false;
}
//Trigger 错过触发时调用
public void triggerMisfired(Trigger trigger) {
String triggerName = trigger.getKey().getName();
System.out.println("Method 333333 " + triggerName + " misfired");
}
//Trigger 被触发并且完成了 Job 的执行时,Scheduler 调用这个方法
public void triggerComplete(Trigger trigger, JobExecutionContext context,
Trigger.CompletedExecutionInstruction triggerInstructionCode) {
String triggerName = trigger.getKey().getName();
System.out.println("Method 444444 " + triggerName + " is complete");
System.out.println("------------");
}
}
/*******************************************************
*
* @author lbf
* @date 2024-12-01 17:10
*******************************************************/
public class MyTriggerListenerTest {
public static void main(String[] args) throws SchedulerException {
// JobDetail
JobDetail jobDetail = JobBuilder.newJob(QuartzJob01.class).withIdentity("job1", "group1").build();
// Trigger
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever()).build();
// SchedulerFactory
SchedulerFactory factory = new StdSchedulerFactory();
// Scheduler
Scheduler scheduler = factory.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
// 创建并注册一个全局的Trigger Listener
scheduler.getListenerManager().addTriggerListener(new MyTriggerListener("myListener1"), EverythingMatcher.allTriggers());
// 创建并注册一个局部的Trigger Listener
scheduler.getListenerManager().addTriggerListener(new MyTriggerListener("myListener2"), KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger1", "gourp1")));
// 创建并注册一个特定组的Trigger Listener
GroupMatcher<TriggerKey> matcher = GroupMatcher.triggerGroupEquals("gourp1");
scheduler.getListenerManager().addTriggerListener(new MyTriggerListener("myListener3"), matcher);
scheduler.start();
}
}
6.3、SchedulerListener
SchedulerListener主要用于监控 Scheduler 的生命周期中是否有关键事件发生,
当 Scheduler 有关键事件发生时 SchedulerListener 会被调用
与Scheduler有关的事件包括:增加一个job/trigger,删除一个job/trigger,scheduler
运行时发送错误,关闭scheduler等。
SchedulerListener示例代码如下:
/*******************************************************
* 调度器监听器
* 该监听器需要实现接口 SchedulerListener
*
* SchedulerListener主要用于监控 Scheduler 的生命周期中是否有关键事件发生,当 Scheduler 有关键事件发生时 SchedulerListener 会被调用
* 与Scheduler有关的事件包括:增加一个job/trigger,删除一个job/trigger,scheduler发生错误,关闭scheduler等。
*
* @author lbf
* @date 2024-12-01 17:20
*******************************************************/
public class MySchedulerListener implements SchedulerListener {
/**
* 部署 JobDetail 时调用
* @param trigger
*/
public void jobScheduled(Trigger trigger) {
String jobName = trigger.getJobKey().getName();
System.out.println(jobName + " has been scheduled");
}
/**
* 卸载 JobDetail 时调用
*
* @param triggerKey
*/
public void jobUnscheduled(TriggerKey triggerKey) {
System.out.println(triggerKey + " is being unscheduled");
}
/**
* 当一个 Trigger的job执行完成再也不会触发的时调用这个方法;
* 此时该 Trigger会被删除(除非该Trigger被持久化到磁盘)
*
* @param trigger
*/
public void triggerFinalized(Trigger trigger) {
System.out.println("Trigger is finished for " + trigger.getJobKey().getName());
}
/**
* 当一个 Trigger 被暂停时调用该方法
* @param triggerKey
*/
public void triggerPaused(TriggerKey triggerKey) {
System.out.println(triggerKey + " is being paused");
}
/**
* 当一个 Trigger组 被暂停时调用该方法
* @param triggerGroup
*/
public void triggersPaused(String triggerGroup) {
System.out.println("trigger group " + triggerGroup + " is being paused");
}
/**
* 当一个 Trigger 从暂停中恢复时调用该方法
*
* @param triggerKey
*/
public void triggerResumed(TriggerKey triggerKey) {
System.out.println(triggerKey + " is being resumed");
}
/**
* 当一个 Trigger组 从暂停中恢复时调用该方法
* @param triggerGroup
*/
public void triggersResumed(String triggerGroup) {
System.out.println("trigger group " + triggerGroup + " is being resumed");
}
/**
* 当一个 JobDetail 任务被添加时调用该方法
* @param jobDetail
*/
public void jobAdded(JobDetail jobDetail) {
System.out.println(jobDetail.getKey() + " is added");
}
/**
* 当一个 JobDetail 任务被删除时调用该方法
* @param jobKey
*/
public void jobDeleted(JobKey jobKey) {
System.out.println(jobKey + " is deleted");
}
/**
* 当一个 JobDetail 任务被暂停时调用该方法
* @param jobKey
*/
public void jobPaused(JobKey jobKey) {
System.out.println(jobKey + " is paused");
}
/**
* 当一组 JobDetail 任务被暂停时调用该方法
* @param jobGroup
*/
public void jobsPaused(String jobGroup) {
System.out.println("job group " + jobGroup + " is paused");
}
/**
* 当一个 JobDetail 任务从暂停中恢复时调用该方法
* @param jobKey
*/
public void jobResumed(JobKey jobKey) {
System.out.println(jobKey + " is resumed");
}
/**
* 当一组 JobDetail 任务从暂停中恢复时调用该方法
* @param jobGroup
*/
public void jobsResumed(String jobGroup) {
System.out.println("job group " + jobGroup + " is resumed");
}
/**
* 在调度器 Scheduler 运行时出现错误时调用该方法
* @param msg
* @param cause
*/
public void schedulerError(String msg, SchedulerException cause) {
System.out.println(msg + cause.getUnderlyingException().getStackTrace());
}
/**
* 当Scheduler处于StandBy模式时,调用该方法
*/
public void schedulerInStandbyMode() {
System.out.println("scheduler is in standby mode");
}
/**
* 当Scheduler启动时,调用该方法
*/
public void schedulerStarted() {
System.out.println("scheduler has been started");
}
public void schedulerStarting() {
System.out.println("scheduler is being started");
}
/**
* 当 Scheduler停止时时,调用该方法
*/
public void schedulerShutdown() {
System.out.println("scheduler has been shutdown");
}
public void schedulerShuttingdown() {
System.out.println("scheduler is being shutdown");
}
/**
* 当 Scheduler中的数据被清除时,调用该方法。
*/
public void schedulingDataCleared() {
System.out.println("scheduler has cleared all data");
}
}
/*******************************************************
* 调度器监听器练习
*
* @author lbf
* @date 2024-12-01 17:55
*******************************************************/
public class MySchedulerListenerTest {
public static void main(String[] args) throws SchedulerException {
// JobDetail
JobDetail jobDetail = JobBuilder.newJob(QuartzJob01.class).withIdentity("job1", "group1").build();
// Trigger
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();
// SchedulerFactory
SchedulerFactory factory = new StdSchedulerFactory();
// Scheduler
Scheduler scheduler = factory.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
// 注册Scheduler Listener
scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener());
scheduler.start();
}
}