大家好,我是小菜,一个渴望在互联网行业做到蔡不菜的小菜。可柔可刚,点赞则柔,白嫖则刚!死鬼~ 看完记得给我来个三连哦!
本文次要介绍
Quartz 的应用
如有须要,能够参考
如有帮忙,不忘 点赞 ❥
微信公众号已开启,小菜良记,没关注的同学们记得关注哦!
一、初识 Quartz
1. 概念
quartz 是一款开源且丰盛个性的 “任务调度库”,可能集成与任何的java 利用,下到独立利用,大到电子商业系统。quartz 就是基于 java 实现的任务调度框架,用于执行你想要执行的任何工作。
什么是 任务调度?任务调度就是咱们零碎中创立了 N 个工作,每个工作都有指定的工夫进行执行,而这种多任务的执行策略就是任务调度。
quartz 的作用就是让任务调度变得更加丰盛,高效,平安,而且是基于 Java 实现的,这样子开发者只须要调用几个接口坐下简略的配置,即可实现上述需要。
2. 外围
1)工作 Job
咱们想要调度的工作都必须实现 org.quartz.job 接口,而后实现接口中定义的 execute() 办法即可
2)触发器 Trigger
Trigger 作为执行工作的调度器。咱们如果想要凌晨 1 点执行备份数据的工作,那么 Trigger 就会设置凌晨 1 点执行该工作。其中 Trigger 又分为 SimpleTrigger 和 CronTrigger 两种
3)调度器 Scheduler
Scheduler 为工作的调度器,它会将工作 Job 及触发器 Trigger 整合起来,负责基于 Trigger 设定的工夫来执行 Job
3. 体系结构
二、实战 Quartz
下面咱们大略介绍了 Quartz,那么该如何应用了,请往下看:
- 导入 Quartz 依赖
<!--quartz-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
- 自定义工作
public class TestJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) {String data = LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("START DATA BACKUP, current time:" + data);
}
}
- 创立任务调度
public class TestScheduler {public static void main(String[] args) throws Exception {
// 获取任务调度的实例
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 定义任务调度实例, 并与 TestJob 绑定
JobDetail job = JobBuilder.newJob(TestJob.class)
.withIdentity("testJob", "testJobGroup")
.build();
// 定义触发器, 会马上执行一次, 接着 5 秒执行一次
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("testTrigger", "testTriggerGroup")
.startNow()
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
.build();
// 应用触发器调度工作的执行
scheduler.scheduleJob(job, trigger);
// 开启工作
scheduler.start();}
}
/** OUTPUT:
START DATA BACKUP, current time:2020-11-17 21:48:30
START DATA BACKUP, current time:2020-11-17 21:48:35
START DATA BACKUP, current time:2020-11-17 21:48:40
START DATA BACKUP, current time:2020-11-17 21:48:45
**/
通过以上示例,咱们胜利执行了一个工作,咱们来回顾一下程序中呈现的几个重要参数:
- Job 和 JobDetail
- JobExecutionContext
- JobDataMap
- Trigger
1. Job 和 JobDetail
1)Job
Job 是工作任务调度的接口,工作类须要实现该接口。该接口中定义了 execute 办法,咱们须要在外面编写工作执行的业务逻辑,相似 JDK 提供的 TimeTask 类的 run办法。每次调度器执行 Job 时,在调用 execute 办法之前都会创立一个新的 Job 实例,当调用实现后,关联的 Job 对象示例会被开释,开释的实例会被垃圾回收机制回收
2)JobDetail
JobDetail 是为 Job 实例提供了许多设置属性,以及 JobDetailMap 成员变量属性,它用来存储特定 Job 实例的状态信息,调度器须要借助 JobDetail 对象来增加 Job 实例。
其中有几个重要属性:
JobDataMap jobDataMap = jobDetail.getJobDataMap();
String name = jobDetail.getKey().getName();
String group = jobDetail.getKey().getGroup();
String jobName = jobDetail.getJobClass().getName();
两者之间的关系:
JobDetail 定义的是工作数据,而真正的执行逻辑是是在 Job 中。这是因为工作是有可能并发执行,如果 Scheduler 间接应用 Job,就会存在对同一个 Job 实例并发拜访的问题。而 采纳JobDetail & Job 形式,Scheduler 每次执行,都会依据 JobDetail 创立一个新的 Job 实例,这样就能够躲避并发访的问题
2. JobExecutionContext
当 Scheduler 调用一个 Job,就会将 JobExecutionContext 传递给 Job 的 execute() 办法。这样子在Job 中就能通过 JobExecutionContext 对象来拜访到 Quartz 运行时候的环境以及 Job 自身的明细数据。
3. JobDataMap
顾名思义 JobDataMap 是一个 Map,它实现了 JDK中的 Map 接口,能够用来存取根本数据类型,也能够用来转载任何可序列化的数据对象,当 Job 实例对象被执行时这些参数对象会传递给它。示例如下:
-
任务调度类:
JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
.usingJobData(“testJobDetail”, “jobDetail 数据寄存 ”)
.withIdentity(“testJob”, “testJobGroup”)
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.usingJobData(“testTrigger”, “trigger 数据寄存 ”)
.withIdentity(“testTrigger”, “testTriggerGroup”)
.startNow()
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
.build();
- Job 工作类:
public class TestJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) {System.out.println(jobExecutionContext.getJobDetail()
.getJobDataMap().get("testJobDetail"));
System.out.println(jobExecutionContext.getTrigger()
.getJobDataMap().get("testTrigger"));
}
}
/** OUTPUT:
jobDetail 数据寄存
trigger 数据寄存
**/
以上咱们是通过 getJobDataMap() 办法来获取 JobDataMap 中的值,咱们还能够应用另外一种形式来获取:
- Job 工作类:
public class TestJob implements Job {
private String testJobDetail;
public void setTestJobDetail(String testJobDetail) {this.testJobDetail = testJobDetail;}
@Override
public void execute(JobExecutionContext jobExecutionContext) {System.out.println(testJobDetail);
}
}
/** OUTPUT:
jobDetail 数据寄存
**/
以上形式便是:只有咱们在 Job 实现类中增加对应 key 的 setter 办法,那么 Quartz 框架默认的 JobFactory 实现类在初始化 Job 实例对象时回主动地调用这些 setter 办法
注: 如果遇到同名的 key,比方咱们在JobDetail 中寄存值的 key 与在 Trigger 中寄存值的 key 雷同,那么最终 Trigger 的值会笼罩掉 JobDetail 中的值,示例如下:
- 任务调度类:两者中都寄存了 key 为 testInfo 的值
JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
.usingJobData("testInfo", "jobDetail 数据寄存")
.withIdentity("testJob", "testJobGroup")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.usingJobData("testInfo", "trigger 数据寄存")
.withIdentity("testTrigger", "testTriggerGroup")
.startNow()
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
.build();
- Job 工作类:会输入 Trigger 中寄存的值
public class TestJob implements Job {
private String testInfo;
public void setTestInfo(String testInfo) {this.testInfo = testInfo;}
@Override
public void execute(JobExecutionContext jobExecutionContext) {System.out.println(testInfo);
}
}
/** OUTPUT:
trigger 数据寄存
**/
4. Job 的状态
如果咱们有个需要是统计每个工作的执行次数,那么你会怎么做?
兴许你会想到应用下面说到的 JobDataMap,那就让咱们尝试下:
- 任务调度类
// 咱们在 JobDataMap 中定义了一个值为 0 的初始值
JobDetail jobDetail = JobBuilder.newJob(TestJob.class)
.usingJobData("executeCount", 0)
.withIdentity("testJob", "testJobGroup")
.build();
- Job 工作类
@Slf4j
public class TestJob implements Job {
private Integer executeCount;
public void setExecuteCount(Integer executeCount) {this.executeCount = executeCount;}
@Override
public void execute(JobExecutionContext jobExecutionContext) {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:38:48
execute count: 1, current time: 2020-11-17 22:38:52
execute count: 1, current time: 2020-11-17 22:38:57
**/
依照下面的想法咱们写出了这部分代码,但貌似打脸了,后果并没有依照咱们预计的倒退,是逻辑不对吗,貌似写的也没什么问题。这时你会不会回顾到下面我讲过的一句话:“ 在调用 execute 办法之前都会创立一个新的 Job 实例 ”,这就牵引出了 Job 状态的概念:
- 无状态的 Job
每次调用时都会创立一个新的 JobDataMap
- 有状态的 Job
屡次 Job 调用能够持有一些状态信息,这些状态信息存储在 JobDataMap 中
那么问题来了,如果让 Job 变成有状态?这个时候咱们能够借助一个注解:@PersistJobDataAfterExecution
,加上这个注解后,咱们再来试下:
Job 工作类:
@Slf4j
@PersistJobDataAfterExecution
public class TestJob 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
**/
能够看到加了 @PersistJobDataAfterExecution,咱们曾经胜利达到了咱们的目标。
5. Trigger
通过以上示例,咱们曾经大略晓得了 Quartz 的组成,咱们定义了工作之后,须要用触发器 Trigger 去指定 Job 的执行工夫,执行距离,运行次数等,那么 Job 与 Trigger 的联合,咱们两头还须要 Scheduler 去调度,三者关系大抵如下:
其中 Trigger 又有几种实现类如下:
大抵有四个实现类,然而咱们平时用的最多的还是 CronTriggerImpl 和 SimpleTriggerImpl
咱们如果想要定义工作何时执行,何时完结,咱们能够这样做:
- 任务调度类
Date startTime = new Date();
startTime.setTime(startTime.getTime() + 5000);
Date endTime = new Date();
endTime.setTime(startTime.getTime() + 10000);
Trigger trigger = TriggerBuilder.newTrigger()
.usingJobData("testInfo", "trigger 数据寄存")
.withIdentity("testTrigger", "testTriggerGroup")
.startNow()
.startAt(startTime)
.endAt(endTime)
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
.build();
-
Job 工作类
@Slf4j
@PersistJobDataAfterExecution
public class TestJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Trigger trigger = jobExecutionContext.getTrigger();
log.info(“start time : {}, end time: {}”,
trigger.getStartTime(), trigger.getEndTime());
}
}
/** OUTPUT:
start time : Thu Nov 17 22:42:51 CST 2020, end time: Thu Nov 17 22:43:01 CST 2020
start time : Thu Nov 17 22:42:51 CST 2020, end time: Thu Nov 17 22:43:01 CST 2020
**/
通过控制台能够看到,工作执行了两次便曾经进行了,因为曾经超过了进行工夫 Thu Nov 17 22:43:01 CST 2020
1)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 具备的属性有:开始工夫、完结工夫、反复次数和反复的工夫距离
- 反复次数 的值能够为 0、 正整数 、 或常量 SimpleTrigger.REPEAT_INDEFINITELY
- 反复的工夫距离属性值必须大于 0 或长整型的正整数,以 毫秒 作为工夫单位,当反复的工夫距离为 0 时,意味着与 Trigger 同时触发执行
- 完结工夫和反复次数同时存在时,以完结工夫优先
2)CronTrigger
跟 SimpleTrigger 执行间隔时间触发的相比,CronTrigger 更加灵便,它是基于日历的作业调度器。应用 CronTrigger 咱们能够执行某个工夫点执行,例如“每天的凌晨 1 点执行”、“每个工作日的 12 点执行”,也能够像 SimpleTrigger 那样执行一个开始工夫和完结工夫运行工作
学习 CronTrigger 之前咱们得先学习 Cron 表达式
- Cron 表达式
Cron 表达式 是用来配置 CronTrigger 实例,它是一个由 7 个子表达式组成的字符串,每个字符都示意不同的日程细节:
Seconds
:秒Minutes
:分钟Hours
:小时Day-of-Month
:月中的哪几天Month
:月Day-of-Week
:周中的哪几天Year
:年
字段 | 是否必填 | 允许值 | 可用特殊字符 |
---|---|---|---|
秒 | 是 | 0-59 | ,– * / |
分 | 是 | 0-59 | ,– * / |
小时 | 是 | 0-23 | ,– * / |
月中的哪几天 | 是 | 1-31 | ,– * / ? L W C |
月 | 是 | 1-12 或 JAN-DEC | ,– * / |
周中的哪几天 | 是 | 1-7 或 SUN-SAT | ,– * / ? L C # |
年 | 否 | 不填写 或 1970-2099 | ,– * / |
- 特殊符号
特殊符号 | 含意 |
---|---|
* | 可用在所有字段中,示意对应工夫域的每一个时刻,例如,* 在分钟字段时,示意“每分钟” |
? | 该字符只在日期和星期字段中应用,它通常指定为“无意义的值”,相当于点位符 |
– | 表白一个范畴,如在小时字段中应用“10-12”,则示意从 10 到 12 点,即 10,11,12 |
, | 表白一个列表值,如在星期字段中应用“MON,WED,FRI”,则示意星期一,星期三和星期五 |
/ | x/ y 表白一个等步长序列,x 为起始值,y 为增量步长值。如在分钟字段中应用 0 /15,则示意为 0,15,30 和 45 秒,而 5 /15 在分钟字段中示意 5,20,35,50,你也能够应用 */y,它等同于 0 /y |
L | 该字符只在日期和星期字段中应用,代表“Last”的意思,但它在两个字段中意思不同。L 在日期字段中,示意这个月份的最初一天,如一月的 31 号,非平年二月的 28 号;如果 L 用在星期中,则示意星期六,等同于 7。然而,如果 L 呈现在星期字段里,而且在后面有一个数值 X,则示意“这个月的最初 X 天”,例如,6L 示意该月的最初星期五 |
W | 该字符只能呈现在日期字段里,是对前导日期的润饰,示意离该日期最近的工作日。例如 15W 示意离该月 15 号最近的工作日,如果该月 15 号是星期六,则匹配 14 号星期五;如果 15 日是星期日,则匹配 16 号星期一;如果 15 号是星期二,那后果就是 15 号星期二。但必须留神关联的匹配日期不可能跨月,如你指定 1W,如果 1 号是星期六,后果匹配的是 3 号星期一,而非上个月最初的那天。W 字符串只能指定繁多日期,而不能指定日期范畴 |
# | 该字符只能在星期字段中应用,示意当月某个工作日。如 6#3 示意当月的第三个星期五(6 示意星期五,#3 示意以后的第三个),而 4#5 示意当月的第五个星期三,假如当月没有第五个星期三,疏忽不触发 |
Cron 表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。
-
实战演习
“0 0 10,14,16 ?” 每天上午 10 点,下午 2 点,4 点
“0 0/30 9-17 ?” 朝九晚五工作工夫内每半小时,从 0 分开始每隔 30 分钟发送一次
“0 0 12 ? * WED” 示意每个星期三中午 12 点
“0 0 12 ?” 每天中午 12 点触发
“0 15 10 ? ” 每天上午 10:15 触发
“0 15 10 ?” 每天上午 10:15 触发
“0 15 10 ? *” 每天上午 10:15 触发
“0 15 10 ? 2005″ 2005 年的每天上午 10:15 触发
“0 14 * ?” 在每天下午 2 点到下午 2:59 期间的每 1 分钟触发
“0 0/55 14 ?” 在每天下午 2 点到下午 2:55 期间,从 0 开始到 55 分钟触发
“0 0-5 14 ?” 在每天下午 2 点到下午 2:05 期间的每 1 分钟触发
“0 10,44 14 ? 3 WED” 每年三月的星期三的下午 2:10 和 2:44 触发
“0 15 10 ? * MON-FRI” 周一至周五的上午 10:15 触发
“0 15 10 15 * ?” 每月 15 日上午 10:15 触发
“0 15 10 L * ?” 每月最初一日的上午 10:15 触发
“0 15 10 ? * 6L” 每月的最初一个星期五上午 10:15 触发
“0 15 10 ? * 6L 2002-2005” 2002 年至 2005 年的每月的最初一个星期五上午 10:15 触发
“0 15 10 ? * 6#3” 每月的第三个星期五上午 10:15 触发
- 应用示例
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("testTrigger", "testTriggerGroup")
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * 6 4 ?"))
.build();
6. Scheduler
Quartz 是以模块的形式构建的,Job 和 Trigger 之间的联合须要靠 Scheduler。
- 创立
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
创立是通过 Quartz 默认的 SchedulerFactory,咱们能够应用自定义参数(Properties)来创立和初始化 Quartz 调度器,配置参数个别存储在 quartz.properties 中。
咱们下面是通过 scheduleJob() 办法来联合 Job和 Trigger,这个办法有个工夫类型的返回值,咱们能够获取到调度器开始的工夫:
Date date = scheduler.scheduleJob(jobDetail, trigger);
关联完工作和触发器,咱们就能够启动任务调度了:
scheduler.start();
将任务调度挂起(暂停):
scheduler.standby();
将工作敞开:
shutdown(true);// 示意期待所有正在执行的 job 执行结束之后,再敞开 Scheduler
shutdown(false);// 示意间接敞开 Scheduler
咱们再来理解下 quartz.properties 文件,先看一个示例:
也能够编写程序代码操作 quartz.properties 文件的内容:
public class QuartzProperties {
public static void main(String[] args) {
// 创立工厂实例
StdSchedulerFactory factory = new StdSchedulerFactory();
// 创立配置工厂的属性对象
Properties props = new Properties();
props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, "org.quartz.simpl.SimpleThreadPool"); // 线程池定义
props.put("org.quartz.threadPool.threadCount", "5"); // 默认 Scheduler 的线程数
try {
// 应用定义的属性初始化工厂
factory.initialize(props);
Scheduler scheduler = factory.getScheduler();
scheduler.start();} catch (SchedulerException e) {e.printStackTrace();
}
}
}
通过 Properties 设置工厂属性的毛病在用硬编码,如果须要批改例子中线程数量,将不得不批改代码,而后从新编译,所以不举荐应用。
三、Quartz 监听器
在 Quartz 实战中咱们理解到三个外围模块别离是 Job、Trigger、Scheduler,既然 Quartz中存在监听器,相应的,这三者也别离有对应的监听器。监听器的作用便是用于当任务调度中你所关注事件产生时,可能及时获取这一事件的告诉
1. JobListener
任务调度中,与工作 Job 相干的事件包含:Job 开始要执行的提醒,执行实现的提醒,接口如下:
1)办法解析
getName()
:用于获取改JobListener 的名称jobToBeExecuted()
:Scheduler 在 JobDetail 将要被执行时调用这个办法`jobExecutionVetoed()
:Scheduler 在 JobDetail 行将被执行,但又被 TriggerListener 否决时会调用该办法jobWasExecuted()
:Scheduler 在 JobDetail 被执行之后调用这个办法
2)示例
- 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 相干的事件包含:触发器触发、触发器未失常触发、触发器实现等:
1)办法解析
getName()
:用于获取触发器的名称triggerFired()
:当与监听器相关联的 Trigger 被触发,Job 上的 execute() 办法将被执行时,Scheduler 就调用该办法vetoJobExecution()
:在 Trigger 触发后,Job 将要被执行时由 Scheduler 调用这个办法。TriggerListener 给了一个抉择去否决 Job 的执行。如果这个办法返回 true,这个 Job 将不会为此次 Trigger 触发而失去执行triggerMisfired()
:Scheduler 调用这个办法是在 Trigger 错过触发时。你应该关注此办法中持续时间长的逻辑:在呈现许多错过触发的 Trigger 时,长逻辑会导致骨牌效应,所以该当放弃这办法尽量的小triggerComplete()
:Trigger 被触发并且实现了 Job 的执行时,Scheduler 调用这个办法。
2)示例
- 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 实现之后触发
**/
3. SchedulerListener
SchedulerListener会在 Scheduler 的生命周期中要害事件产生时被调用。与 Scheduler 无关的事件包含:减少一个 job/trigger,删除一个job/trigger,scheduler 产生严重错误,敞开 scheduler 等。
1)办法解析
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 中的数据被革除时,调用该办法。
2)示例
- 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 删除工作工作
**/
通过以上三个监听器的示例,咱们也能够大略理解到监听器执行的机会,具体的咱们能够实际上手操练一番
END
这篇 Quartz 的介绍就告一段落啦,看到这里的你心愿有所播种哦!路漫漫,小菜与你一起求索!
明天的你多致力一点,今天的你就能少说一句求人的话!
我是小菜,一个和你一起学习的男人。
????
微信公众号已开启,小菜良记,没关注的同学们记得关注哦!