一、初识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,那么该如何使用了,请往下看:
1 2 3 4 5 6
| <!--quartz--> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version> </dependency>
|
1 2 3 4 5 6 7 8
| 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); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class TestScheduler { public static void main(String[] args) throws Exception { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail job = JobBuilder.newJob(TestJob.class) .withIdentity("testJob", "testJobGroup") .build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("testTrigger", "testTriggerGroup") .startNow() .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)) .build(); scheduler.scheduleJob(job, trigger); scheduler.start(); } }
|
通过以上示例,我们成功执行了一个任务,我们来回顾一下程序中出现的几个重要参数:
-
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实例。
其中有几个重要属性:
1 2 3 4
| 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实例对象被执行时这些参数对象会传递给它。示例如下:
1 2 3 4 5 6 7 8 9 10
| 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();
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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")); } }
|
以上我们是通过getJobDataMap( )方法来获取JobDataMap中的值,我们还可以使用另外一种方式来获取:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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); } }
|
以上方式便是:只要我们在Job实现类中添加对应key的setter方法,那么Quartz框架默认的JobFactory实现类在初始化 Job 实例对象时回自动地调用这些 setter 方法
注:如果遇到同名的 key,比如我们在JobDetail中存放值的key与在Trigger中存放值的key相同,那么最终Trigger的值会覆盖掉JobDetail中的值,示例如下:
- 任务调度类:两者中都存放了key为testInfo的值
1 2 3 4 5 6 7 8 9 10
| 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();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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); } }
|
4. Job 的状态
如果我们有个需求是统计每个任务的执行次数,那么你会怎么做?

也许你会想到使用上面说到的JobDataMap,那就让我们尝试下:
1 2 3 4 5
| JobDetail jobDetail = JobBuilder.newJob(TestJob.class) .usingJobData("executeCount", 0) .withIdentity("testJob", "testJobGroup") .build();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @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); jobExecutionContext.getJobDetail() .getJobDataMap().put("executeCount", executeCount); } }
|
按照上面的想法我们写出了这部分代码,但貌似打脸了,结果并没有按照我们预计的发展,是逻辑不对吗,貌似写的也没什么问题。这时你会不会回忆到上面我讲过的一句话:“在调用 execute 方法之前都会创建一个新的 Job 实例”,这就牵引出了Job状态的概念:
每次调用时都会创建一个新的JobDataMap
多次Job调用可以持有一些状态信息,这些状态信息存储在JobDataMap中
那么问题来了,如果让Job变成有状态?这个时候我们可以借助一个注解:@PersistJobDataAfterExecution,加上这个注解后,我们再来试下:
Job 任务类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @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); jobExecutionContext.getJobDetail(). getJobDataMap().put("executeCount", executeCount); } }
|
可以看到加了**@PersistJobDataAfterExecution**,我们已经成功达到了我们的目的。
5. Trigger
经过以上示例,我们已经大概知道了Quartz的组成,我们定义了任务之后,需要用触发器Trigger去指定Job的执行时间,执行间隔,运行次数等,那么Job与Trigger的结合,我们中间还需要Scheduler去调度,三者关系大致如下:

其中Trigger又有几种实现类如下:

大致有四个实现类,但是我们平时用的最多的还是CronTriggerImpl和SimpleTriggerImpl
我们如果想要定义任务何时执行,何时结束,我们可以这样做:
1 2 3 4 5 6 7 8 9 10 11 12
| 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();
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @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()); } }
|
通过控制台可以看到,任务执行了两次便已经停止了,因为已经超过了停止时间Thu Nov 17 22:43:01 CST 2020
1)SimpleTrigger
我们上面看到示例,用到的都是SimpleTrigger,SimpleTrigger对于设置和使用是最为简单的一种QuartzTrigger,它是为那种需要在特定的日期/时间启动,且以一个可能的间隔时间重复执行n次的Job任务所设计的。
比如我想要在一个指定的时间段内执行一次任务,我们只需要这样写:
1 2 3 4
| Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("testTrigger", "testTriggerGroup") .startAt(startTime) .build();
|
再者我想在指定的时间间隔内多次执行该任务,我们可以这样写:
1 2 3 4 5
| Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("testTrigger", "testTriggerGroup") .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)) .withRepeatCount(2)) .build();
|
我们来总结一下上面的示例:
-
SimpleTrigger具备的属性有:开始时间、结束时间、重复次数和重复的时间间隔
-
重复次数的值可以为0、正整数、或常量 SimpleTrigger.REPEAT_INDEFINITELY
-
重复的时间间隔属性值必须大于0或长整型的正整数,以毫秒作为时间单位,当重复的时间间隔为0时,意味着与Trigger同时触发执行
-
结束时间和重复次数同时存在时,以结束时间优先
2)CronTrigger
跟SimpleTrigger执行间隔时间触发的相比,CronTrigger更加灵活,它是基于日历的作业调度器。使用CronTrigger我们可以执行某个时间点执行,例如 “每天的凌晨1点执行”、“每个工作日的 12 点执行”,也可以像SimpleTrigger那样执行一个开始时间和结束时间运行任务
学习CronTrigger之前我们得先学习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表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| "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触发
|
1 2 3 4
| Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("testTrigger", "testTriggerGroup") .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * 6 4 ?")) .build();
|
6. Scheduler
Quartz是以模块的方式构建的,Job和Trigger之间的结合需要靠Scheduler。
1
| Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
|
创建是通过Quartz默认的SchedulerFactory,我们可以使用自定义参数(Properties)来创建和初始化Quartz调度器,配置参数一般存储在quartz.properties中。
我们上面是通过scheduleJob()方法来结合Job和Trigger,这个方法有个时间类型的返回值,我们可以获取到调度器开始的时间:
1
| Date date = scheduler.scheduleJob(jobDetail, trigger);
|
关联完任务和触发器,我们就可以启动任务调度了:
将任务调度挂起(暂停):
将任务关闭:
shutdown(true);//表示等待所有正在执行的job执行完毕之后,再关闭Scheduler shutdown(false);//表示直接关闭Scheduler
我们再来了解下quartz.properties文件,先看一个示例:

也可以编写程序代码操作quartz.properties文件的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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");
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)示例
1 2 3 4 5 6 7 8 9
| @Slf4j @PersistJobDataAfterExecution public class TestJob implements Job {
@Override public void execute(JobExecutionContext jobExecutionContext){ System.out.println("TestJob 执行啦"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 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被执行之后调用这个方法"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @Slf4j public class TestScheduler { public static void main(String[] args) throws Exception { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = JobBuilder.newJob(TestJob.class) .usingJobData("executeCount", 0) .withIdentity("testJob", "testJobGroup") .build(); Trigger trigger = TriggerBuilder.newTrigger() .usingJobData("testInfo", "trigger数据存放") .withIdentity("testTrigger", "testTriggerGroup") .startNow() .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)) .build(); scheduler.getListenerManager() .addJobListener(new MyJobListener(), EverythingMatcher.allJobs());
scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); } }
|
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)示例
1 2 3 4 5 6 7 8 9
| @Slf4j @PersistJobDataAfterExecution public class TestJob implements Job {
@Override public void execute(JobExecutionContext jobExecutionContext){ System.out.println("TestJob 执行啦"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| 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; }
@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 + " 完成之后触发"); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Slf4j public class TestScheduler { public static void main(String[] args) throws Exception { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = JobBuilder.newJob(TestJob.class) .usingJobData("executeCount", 0) .withIdentity("testJob", "testJobGroup") .build(); Trigger trigger = TriggerBuilder.newTrigger() .usingJobData("testInfo", "trigger数据存放") .withIdentity("testTrigger", "testTriggerGroup") .startNow() .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)) .build();
scheduler.getListenerManager().addTriggerListener(new MyTriggerListener("simpleTrigger"), EverythingMatcher.allTriggers());
scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); } }
|
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)示例
1 2 3 4 5 6 7 8 9
| @Slf4j @PersistJobDataAfterExecution public class TestJob implements Job {
@Override public void execute(JobExecutionContext jobExecutionContext){ System.out.println("TestJob 执行啦"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| 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("调度器的数据被清除时调用"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| @Slf4j public class TestScheduler { public static void main(String[] args) throws Exception { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobDetail jobDetail = JobBuilder.newJob(TestJob.class) .usingJobData("executeCount", 0) .withIdentity("testJob", "testJobGroup") .build(); 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();
scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener());
scheduler.scheduleJob(jobDetail, trigger); scheduler.start(); } }
|
通过以上三个监听器的示例,我们也可以大概了解到监听器执行的时机,具体的我们可以实际上手操练一番
来自: Quartz