Quartz
Quartz概念
基本概念
起源
Quartz是OpenSymphony开源组织在Job scheduling(任务调度)领域又一个开源项目
它可以与J2EE或J2SE应用程序相结合,也可以单独使用
特点
Quartz是开源且具有丰富特性的“任务调度库”,能够集成于任何的java应用,小到独立的应用,大至电子商业系统
quartz都能创建亦简单亦复杂的调度,以执行上十、上百,甚至上万的任务
任务job被定义为标准的java组件,能够执行任何你想要实现的功能
quartz调度框架包含许多企业级的特性,如JTA事务、集群的支持。
总结
简而言之,quartz就是基于java实现的任务调度框架,用于执行你想要执行的任何任务。
官方网站
http://www.quartz-scheduler.org/
Quartz运行环境
- Quartz可以运行嵌入在另一个独立式应用程序
- Quartz可以在应用程序服务器(或servlet容器)内被实例化,并且参与事务
- Quartz可以作为一个独立的程序运行(其自己的Java虚拟机内),可以通过RMI使用
- Quartz可以被实例化,作为独立的项目集群(负载均衡和故障转移功能),用于作业的执行
Quartz设计模式(官方示例)
- Builder模式
// 任务 JobbDetail job = newJob(MyJob.class).withIdentity("job1","group1").build(); // 触发器 Trigger trigger = newTrigger().withIdentity("trigger1","group1").startNow() .withSchedule(simpleSchedule().withIntervalInSeconds(40).repeaForevver()).build();
- Factory模式
// quartz获取一个调度器 Schedulere scheduler = StdSchedulerFactory.getDefaultScheduler(); // 启动或关闭调度器 scheduler.start();
- 组件模式
// 任务组件 JobDetail job = newJob(MyJob.class).withIdentity("job1","group1").build(); // 触发器组件 Trigger trigger = newTrigger().withIdentity("trigger1","group1").startNow() .withSchedule(simpleSchedule().withIntervalInSeconds(40).repeaForevver()).build(); // 调度器组件 scheduler.scheduleJob(job,trigger);
- 链式编程
Trigger trigger = newTrigger().withIdentity("trigger1","group1").startNow() .withSchedule(simpleSchedule().withIntervalInSeconds(40).repeaForevver()).build();
Quartz核心概念
任务Job
Job就是你想要实现的任务类,每一个Job必须实现org.quartz.job接口,且只需要实现接口定义的execute()方法
触发器Trigger
Trigger为你执行任务的触发器,比如你想要每天定时3点钟发送一份统计邮件,Trigger将会设置3点进行执行该任务
Trigger主要包含两种SimpleTrigger和CronTrigger两种
调度器Scheduler
Scheduler为任务的调度器,它会将任务job及触发器Trigger整合起来,负责基于Trigger设定的时间来执行job
Quartz体系结构
Quartz常用API
以下是Quartz编程API几个重要接口,也是Quartz的重要组件
- Scheduler 用于与调度程序交互的主要程序接口。
Scheduler调度程序-任务执行计划表,,只有安排进执行计划的任务Job(通过scheduler.scheduleJon方法安排执行计划),当它预先定义的执行条件到了的时候(任务触发trigger),该任务才会执行。 - Job 我们预先定义的希望在未来时间能被调度程序执行的任务类,我们可以自定义。
- JobDetail 使用JobDetail来定义定时任务的实例,JobDetail实例是通过JobBuilder类创建的。
可以理解为JobDetail就是前面Job的一个实例 - JobDataMap 可以包含不限量的(序列化的)数据对象,在Job实例执行的时候,可以使用其中的数据;
JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本数据类型的数据的方法。 - Trigger 触发器,Trigger对象是用来触发执行Job的。
当调度一个Job时,我们实例一个触发器然后调整它的属性来满足Job执行的条件。
表明任务在什么时候会执行。定义了一个已经被安排的任务将会在什么时候执行的时间条件,比如每2秒就执行一次 - JobBuilder 用于声明一个任务示例,也可以定义关于该任务的详情比如任务名、组名等,这个声明的实例将会作为一个实际执行的任务。
实际就是通过JobBuilder创建Job的实例,也就是JobDetail。 - TriggerBuilder 触发器创建器,用于创建触发器trigger实例。
- JobListener、TriggerListener、SchedulerListener 监听器,用于对组件的监听。
Quartz基本使用
准备工作
创建Maven工程
引入依赖
<!-- quartz核心包 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
<!-- quartz工具包 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.2</version>
</dependency>
<!-- slf4j-log4j整合包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
<scope>test</scope>
</dependency>
<!-- log4j日志包 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
指定项目编译环境
<plugin>
<gropuId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<target>1.8</target>
<source>1.8</source>
</configuration>
</plugin>
引入log4j配置文件(log4j.properties)
### log日志输出到控制台 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### log日志输出到文件 ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L -%m%n
### 设置log4j级别(默认为debug级别,修改为info级别)以及使用上面那种方式,下面是输出到控制台 ###
log4j.rootLogger=info,stdout
入门案例
定义一个任务类(实现Job接口)
任务类就是定义我们具体需要执行的任务
此处我们定义就是打印当前时间
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("任务类:具体需要执行的任务");
System.out.println("当前时间为:"+new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date()));
}
}
创建任务调度类(Demo)
实现步骤
- 从工厂中获取一个调度器备用
- 通过JobBuilder创建一个任务实例JobDetail
绑定自定义任务类,也就是实现了Job接口的类
设置名称以及分组名称,然后构建实例 - 通过TriggerBuilder创建一个触发器实例Trigger
设置触发器名称以及分组名称
设置触发器启动时间,可以给定时间,也可以立即启动
设置具体的触发器类型,且设置触发条件,然后进行构建 - 通过前面的调度器关联任务实例和触发器
- 启动调度器,也可以停止shutDown,或者暂停
示例代码
public class Demo {
public static void main(String[] args) throws SchedulerException {
// 1.调度器(Scheduler)从工厂中获取一个任务调度的实例
// 此处获取默认的调度器(默认:实例化new StdSchedulerFactory()后获取)
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 2. 任务实例(JobDetail)
// 加载任务类,与HelloJob完成绑定,要求HelloJob实现Job接口
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
// 参数1(自定义):任务的名称(唯一实例) 参数2(自定义):任务组的名称,可以将任务分组执行
.withIdentity("job1", "group1")
.build();
// 3. 触发器(Trigger)
// 加载触发器
Trigger trigger = TriggerBuilder.newTrigger()
// 参数1(自定义):触发器名称(唯一实例)
// 参数2(自定义):触发器组名称,触发器也可分组
.withIdentity("trigger1", "group1")
// 设置触发器启动,可以指定时间,也可以马上启动,下面就是马上启动
.startNow()
// 触发器两种任选一种,选择执行方式为重复执行,按照秒重复,时间为5秒
// 该方法为静态方法,直接通过类调用,如果是普通方法,创建实例SimpleScheduleBuilder.simpleSchedule()
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
.build();
// 4. 通过调度器关联任务和触发器,保证任务按照触发器定义的条件执行
scheduler.scheduleJob(jobDetail,trigger);
// 5. 启动调度器
scheduler.start();
}
}
实现效果
Job和JobDetail介绍
- Job:工作任务调度的接口,任务类需要实现该接口。该接口中定义execute方法类似JDK提供的TimeTask类的run方法。在里面编写任务执行的业务逻辑。
- Job实例在Quartz中的生命周期:每次调度器执行job时,它在调用execute方法前会创建一个新的job实例(通过构造方法得知,每次执行execute方法前会创建job实例),当调用完execute方法后,关联的Job对象实例会被释放,释放的实例会被垃圾回收机制回收。
- JobDetail:JobDetail为Job实例提供了许多设置属性,以及jobDetaMap成员变量属性,它用来存储特定的job实例的状态信息调度器需要借助JobDetail对象来添加Job实例。
- JobDetail重要属性:name、group、jobClass、jobDataMap
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) // 参数1(自定义):任务的名称(唯一实例) 参数2(自定义):任务组的名称,可以将任务分组执行 .withIdentity("job1", "group1") .build(); System.out.println("name:"+jobDetail.getKey().getName()); System.out.println("group:"+jobDetail.getKey().getGroup()); System.out.println("jobClass:"+jobDetail.getJobClass().getName()); // 如果没有设置组名,会有默认的组名,为DEFAULT JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) // 参数1(自定义):任务的名称(唯一实例) 参数2(自定义):任务组的名称,可以将任务分组执行 .withIdentity("job1") .build(); System.out.println("group:"+jobDetail.getKey().getGroup());// DEFAULT
JobExecutionContext介绍
- 当Scheduler调用一个job,会将jobExecutionContext传递给job的execute()方法
public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println("任务类:具体需要执行的任务"); System.out.println("当前时间为:"+new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date())); } }
- job能通过JobExecutionContext对象访问到Quartz运行时环境以及job本身的明细数据(明细数据通过JobDataMap传递获取,详细看下面Map使用)
public class HelloJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { /* * 获取JobDetail的内容,通过JobExecutionContext */ JobDetail jobDetail = jobExecutionContext.getJobDetail(); JobKey key = jobDetail.getKey(); System.out.println("工作任务名称:"+key.getName()); System.out.println("工作任务的组:"+key.getGroup()); System.out.println("任务类的名称(路径)"+jobDetail.getJobClass().getName()); System.out.println("任务类的名称:"+jobDetail.getJobClass().getSimpleName()); System.out.println("任务类:具体需要执行的任务"); System.out.println("当前时间为:"+new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date())); } }
JobDataMap介绍
使用Map获取
- 在进行任务调度时,JobDataMap存储在JObExecutionContext中,非常方便获取。
- JobDataMap可以用来装载任何可序列化的数据对象,,当Job实例对象被执行时这些参数对象会传递给它。
- JobDataMap实现了JDK的Map接口,并且添加了非常方便的方法用来存取基本数据类型。
初始化触发器及JobDetail时存入值
- 存入数据时,JobDetail中的键值对与Trigger中的键值对可以重复,不会被覆盖
public class Demo {
public static void main(String[] args) throws SchedulerException {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1")
// 构建JobDetail时在JobDataMap中存入键值对
.usingJobData("我是键","我是值")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
// 构建触发器时在 JobDataMap中存入键值对
.usingJobData("我是trigger键","我是trigger值")
.build();
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
}
任务实现类中取值
- 获取值时,因为JobDetail和Trigger都存入的值,所以要分别获取
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
/*
* 获取JobDetail的内容,通过JobExecutionContext
*/
JobDetail jobDetail = jobExecutionContext.getJobDetail();
// 通过JobDetail获取其中的JobDataMap,然后获取其中的键值对
JobDataMap jobDataMap = jobDetail.getJobDataMap();
String string = jobDataMap.getString("我是键");
System.out.println(string);
/*
* 获取Trigger的内容,通过JobExecutionContext
*/
Trigger trigger = jobExecutionContext.getTrigger();
// 通过Trigger获取其中的JobDataMap,然后获取键值对
JobDataMap triggerJobDataMap = trigger.getJobDataMap();
String string1 = triggerJobDataMap.getString("我是trigger键");
System.out.println(string1);
System.out.println("任务类:具体需要执行的任务");
System.out.println("当前时间为:"+new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date()));
}
}
setter方法取值
- Job实现类中添加setter方法,对应JobDataMap中的键名
- Quartz框架默认的JobFactory实现类在初始化Job实例对象时会自动调用这些setter方法。
setter方法直接赋值取值
public class HelloJob implements Job {
// 我们在JobDetail或Trigger创建时,在JobDataMap存入了键值对
// 此处我们可以通过定义同键名的setter方法,系统会自动进行赋值操作
private String 我是键;
public void set我是键(String 我是键) {
this.我是键 = 我是键;
}
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// setter会直接进行赋值,此处我们直接打印即可
System.out.println(我是键);
System.out.println("任务类:具体需要执行的任务");
System.out.println("当前时间为:"+new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date()));
}
}
注意点
- 如果在JobDetail和Trigger中存入了同名的Key
- 这种情况下,我们通过第一种方式取值没有问题
- 但是通过setter方法取值时,因为无法指定从Trigger取值或从JobDetail取值
- 所以,遇到键同名的情况,setter的方式会将trigger中的值覆盖JobDetail中的值
JobExecutionContext获取其他值
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 获取JobDetail
JobDetail jobDetail = jobExecutionContext.getJobDetail();
System.out.println("任务的唯一名称:"+jobDetail.getKey().getName());
System.out.println("任务的组的名称:"+jobDetail.getKey().getGroup());
// 获取Trigger
Trigger trigger = jobExecutionContext.getTrigger();
System.out.println("trigger唯一名称:"+trigger.getKey().getName());
System.out.println("trigger组的名称:"+trigger.getKey().getGroup());
// 获取其他值
System.out.println("获取当前任务的执行时间" + jobExecutionContext.getFireTime());
System.out.println("获取下次任务的执行时间" + jobExecutionContext.getNextFireTime());
System.out.println("任务类:具体需要执行的任务");
System.out.println("当前时间为:"+new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date()));
}
}
有状态的Job和无状态的Job
@PersistJobDataAfterExecution注解的使用
- 有状态的Job可以理解为多次Job调用期间可以持有一些状态信息,这些状态信息存储在JobDataMap中,也可以认为就是多个job会共享JobDataMap中的数据,这多次任务之间会有联动。
- 而默认的无状态job每次调用时会创建一个新的JobDataMap。也就是每次新的任务都睡初始化所有数据
无状态演示(无注解)
在JobDetail中存入一个count键值对
public class Demo {
public static void main(String[] args) throws SchedulerException {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1")
// 构建JobDetail时在JobDataMap中存入键值对
.usingJobData("count",0)
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
.build();
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
}
}
在job中取出且自增,重新存入,是在count的自增
public class HelloJob implements Job {
// 通过setter取出count值
private Integer count;
public void setCount(Integer count) {
this.count = count;
}
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// count自增
++count;
System.out.println("count的值为:"+count);
// 重新存入到JobDataMap中
jobExecutionContext.getJobDetail().getJobDataMap().put("count",count);
System.out.println("任务类:具体需要执行的任务");
System.out.println("当前时间为:"+new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date()));
}
}
查看结果,多次任务都会重新初始化数据,数据没有实现自增
有状态演示(在job实现类添加注解,其他代码不动)
@PersistJobDataAfterExecution
public class HelloJob implements Job {
// 通过setter取出count值
private Integer count;
public void setCount(Integer count) {
this.count = count;
}
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// count自增
++count;
System.out.println("count的值为:"+count);
// 重新存入到JobDataMap中
jobExecutionContext.getJobDetail().getJobDataMap().put("count",count);
System.out.println("任务类:具体需要执行的任务");
System.out.println("当前时间为:"+new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date()));
}
}
查看结果,多次任务都会共享数据,实现自增
结论
Job实现类中没有添加@PersistJobDataAfterExecution注解,每次调用时都会创建一个新的的JobDataMap,不会累加
Job实现类中添加@PersistJobDataAfterExecution注解,每次Job调用期间可以持有一些状态信息,即可以实现count的累加。
Trigger介绍
Quartz有一些不同的触发器类型,不过,用得最多的是SimpleTrigger和CronTrigger
- jobKey:表示job实例的标识,触发器被触发时,该指定的job实例会 被执行
- startTime:表示触发器的时间表,第一次开始被触发的时间,它的数据类型是java.util.Date
- endTime:指定触发器终止被触发的时间,它的数据类型是java.util.Date
触发器案例
任务类(获取上面三个参数)
@PersistJobDataAfterExecution
public class HelloTriggerJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("我是任务内容,当前时间为" + format.format(new Date()));
// 获取jobKey、startTime和endTime三个参数
Trigger trigger = jobExecutionContext.getTrigger();
System.out.println("jobKey为:" + trigger.getJobKey());
System.out.println("jobName为:" + trigger.getJobKey().getName());
System.out.println("jobGroup为:" + trigger.getJobKey().getGroup());
System.out.println("任务的开始时间:" + format.format(trigger.getStartTime()));
System.out.println("任务的结束时间:" + format.format(trigger.getEndTime()));
}
}
执行类(设置上面三个参数)
public class HelloTriggerDemo {
public static void main(String[] args) throws SchedulerException {
// 1. 创建调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 设置任务开始时间变量,当前时间3秒后开始执行
Date startTime = new Date();
startTime.setTime(startTime.getTime()+3000);
// 设置任务结束时间变量,当前时间10秒后结束整个任务
Date endTime = new Date();
endTime.setTime(endTime.getTime()+10000);
// 2. 创建任务实例
JobDetail jobDetail = JobBuilder.newJob(HelloTriggerJob.class)
.withIdentity("job1", "group1")
.build();
// 3. 创建触发器
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
// 设置触发器,没3秒执行一次任务
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(3))
// 设置任务开始时间
.startAt(startTime)
// 设置任务结束时间
.endAt(endTime)
.build();
// 4. 调度器绑定任务和触发器
scheduler.scheduleJob(jobDetail,trigger);
// 5. 启动调度器
scheduler.start();
}
}
结果分析
- 任务在设定的时间才开始执行,不会跟之前一样立即执行
- 任务在结束时间之后不再执行,虽然设置了每3秒执行一次,到了结束时间后不再执行
- 虽然任务不再执行,但是整个程序不会停止运行
SimpleTrigger触发器
SimpleTrigger对于设置和使用是最为简单的一种QuartzTrigger
它是为那种需要在特定的日期/时间启动,且以一个可能的间隔时间重复执行n次的Job所设计的
案例展示
案例一:在一个指定的时间段内,执行一次作业任务
public class SimpleTriggerDemo {
public static void main(String[] args) throws SchedulerException {
// 创建调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 创建任务实例
JobDetail jobDetail = JobBuilder.newJob(SimpleTriggerJob.class)
.withIdentity("name", "group")
.build();
// 创建触发器,立即执行,执行一次结束
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger", "group")
.startNow()
.build();
// 绑定
scheduler.scheduleJob(jobDetail,trigger);
// 启动
scheduler.start();
}
}
案例二:在指定的时间间隔内多次执行作业任务
- 设置时间间隔,不限次数
public class SimpleTriggerDemo { public static void main(String[] args) throws SchedulerException { // 创建调度器 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 创建任务实例 JobDetail jobDetail = JobBuilder.newJob(SimpleTriggerJob.class) .withIdentity("name", "group") .build(); // 创建触发器 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger", "group") .startNow() // 触发器每3秒执行一次,不限次数 .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(3)) .build(); // 绑定 scheduler.scheduleJob(jobDetail,trigger); // 启动 scheduler.start(); } }
- 设置时间间隔,限制次数(次数默认值是0,2代表执行三次)
public class SimpleTriggerDemo { public static void main(String[] args) throws SchedulerException { // 创建调度器 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 创建任务实例 JobDetail jobDetail = JobBuilder.newJob(SimpleTriggerJob.class) .withIdentity("name", "group") .build(); // 创建触发器 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger", "group") .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule() // 触发器每3秒执行一次,执行3次结束 .repeatSecondlyForever(3).withRepeatCount(2)) .build(); // 绑定 scheduler.scheduleJob(jobDetail,trigger); // 启动 scheduler.start(); } }
案例三:指定任务的结束时间
public class SimpleTriggerDemo {
public static void main(String[] args) throws SchedulerException {
// 创建调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 创建任务实例
JobDetail jobDetail = JobBuilder.newJob(SimpleTriggerJob.class)
.withIdentity("name", "group")
.build();
// 设置任务结束时间变量,当前时间10秒后结束整个任务
Date endTime = new Date();
endTime.setTime(endTime.getTime()+10000);
// 创建触发器
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger", "group")
.startNow()
// 设置结束时间,优先次数,也就是到了结束时间就结束,无论执行次数
// 有没有达到设置的值
.endAt(endTime)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
// 触发器每3秒执行一次,执行3次结束
.repeatSecondlyForever(3).withRepeatCount(2))
.build();
// 绑定
scheduler.scheduleJob(jobDetail,trigger);
// 启动
scheduler.start();
}
}
注意点
- SimpleTrigger的属性有:开始时间、结束时间、重复次数和重复的时间间隔
- 重复次数属性的值可以为0、正整数、或常量SimpleTrigger.REPEAT_INDEFINITELY。
- 重复的时间间隔属性必须为大于0或长整形的正整数,以毫秒作为时间单位,当重复的时间间隔为0时,意味着与Trigger同时触发执行,但是会报错
- 如果指定结束时间属性值,则结束时间属性优先于重复次数属性,这样的好处在于:当我们需要创建一个每隔10秒触发一次直到指定的结束时间的Trigger,而无需去计算从开始到结束的所有重复次数,我们只需要简单的指定结束时间和使用REPEAT_INDEFINITELY作为重复次数的属性值即可
CronTrigger触发器
概念
如果你需要想日历那样按日程来触发任务,而不是想SimpleTrigger那样每隔特定的间隔时间触发,CronTrigger通常比SimpleTrigger更有用,因为它是基于日历的作业调度器。
使用CronTrigger,你可以指定诸如“每个周五中午”,或者“每个工作日的9:30”或者“从每个周一、周三、周五的上午9:00到上午10:00之间每隔五分钟”这样日程安排来触发。
甚至,像SimpleTrigger一样,CronTrigger也有一个startTime以指定日程从什么时候开始,也有一个(可选的)endTime以指定何时日程不再继续。
Cron Expressions–Cron表达式
概念
Cron表达式被用来配置CronTrigger实例。Cron表达式是一个由7个子表达式组成的字符串。每个子表达式都描述了一个单独的日程细节。这些子表达式用空格分隔,分别表示:
表示
- Seconds 秒
- Minutes 分钟
- Hours 小时
- Day-of-Month 月中的天
- Month 月
- Day-of-Week 周中的天
- Year(optional field)年(可选的)
取值
字段 | 是否必填 | 允许值 | 运行的特殊字符 |
---|---|---|---|
秒 | 是 | 0-59 | ,- * / |
分 | 是 | 0-59 | , - * / |
时 | 是 | 0-23 | , - * / |
日 | 是 | 1-31 | , - * / ? L W C |
月 | 是 | 1-12或者JAN-DEC | , - * / |
周 | 是 | 1-7或者SUN-SAT | , - * / ? L C # |
年 | 否 | 不填写,或者1970-2099 | , - * / |
单个子表达式可以包含范围或者列表。
例如:前面例子中的周中的天这个域的合法值为0到59,小时的合法范围是0到23,Day-of-Month中值的合法范围是1到31,但是需要注意不同的月份中的天数不同
月份的合法值是1到12.或者用字符串JAN,FEB MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV及DEC来表示
Days-of-Week可以用1到7来表示(1=星期日)或者用字符串SUN,MON,TUE,THU,FRI和SAT来表示
特殊符号 | 含义 |
---|---|
* | 用来表示域中的“每个”可能的值。因此在“Month”域中的*表示每个月,而在Day-of-Week域中的*则表示“周中的每一天” |
? | 表示不指定值,使用的场景为不需要关系当前设置这个字段的值。因为“月份中国的日期”和星期中的日期这两个元素是互斥的,因此应该通过设置一个问号(?)来表明你不想设置的那个字段 |
- | 表示区间,例如在小时上设置10到12,表示10,11,12点都会触发 |
, | 表示指定多个值,例如在周字段上设置“MON,WED,FRI”,表示周一、周三、周五触发 |
/ | 表示值的增量,例如,如果分钟域中放入‘0/15’,它表示“每隔15分钟,从0开始”,如果在分钟域中使用‘3/20’,则表示“小时中每隔20分钟,从第3分钟开始”,或者另外相同的形式就是‘3,23,43’ |
L | 可以在day-of-month及day-of-week中使用,这个字符是“last”的简写,但是在两个域中的意义不同。例如,在day-of-month域中的“L”表示这个月的最后一天,即,一月的31日,非闰年的二月的28日。如果用在day-of-week中,则表示“7”或者“SAT”,但是如果在day-of-week域中,这个字符跟在别的值后面,则表示“当月的最后周XXX”,例如:“6L”或者“FRIL”都表示本月的最后一个周五。当使用‘L’选项时,最重要的是不要指定列表或者值范围,否则会导致混乱 |
W | 用来指定距离给定日最接近的周几(在day-of-week域中指定),例如:如果你为day-of-month域指定为“15W”,则表示“距离月中15号最近的周几” |
# | 表示月中的第几个周几。例如:day-of-week域中的“6#3”或者“FRI#3”表示“月中第三个周五” |
表达式生成器
有很多的cron表达式在线生成器,下面两个常用仅做参考:
在线Cron表达式生成器
或者
http://www.pdtools.net/tools/becron.jsp
练习
*/5 * * * * ? 每隔5秒执行一次
0 */1 * * * ? 每隔1分钟执行一次
0 0 5-15 * * ? 每天5-15点整点触发
0 0/3 * * * ? 每三分钟触发一次
0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0 12 ? * WED 表示每个星期三中午12点
0 0 17 ? * TUES,THUR,SAT 每周二、四、六下午五点
0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
0 0 23 L * ? 每月最后一天23点执行一次
0 15 10 L * ? 每月最后一日的上午10:15触发
"15,30,45 * * * * ?" 每15秒,30秒,45秒时触发任务
"15-45 * * * * ?" 15到45秒内,每秒都触发任务
"0 15 10 LW * ?" 每个月最后一个工作日的10点15分0秒触发任务
"0 15 10 ? * 5L" 每个月最后一个星期四的10点15分0秒触发任务
"0 15 10 ? * 5#3" 每个月第三周的星期四的10点15分0秒触发任务
案例
- 定义任务HelloJobCronTrigger.java
public class HelloJobCronTrigger implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 定义时间 Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = dateFormat.format(date); // 定义工作任务内容 System.out.println("进行数据库备份操作。当前 任务执行的时间:"+dateString); } }
- 触发器执行HelloSchedulerDemoCronTrigger.java
public class HelloSchedulerDemoCronTrigger { public static void main(String[] args) throws Exception { // 1:从工厂中获取任务调度实例 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口 JobDetail job = JobBuilder.newJob(HelloJobCroTrigger.class) .withIdentity("job1","group1") //定义实例唯一标识 .build(); // 3:定义触发器,马上执行,然后每5秒重复执行一次 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1","group1") //定义该实例唯一标识 .whthSchedule(CronScheduleBuilder.cronnSchedule("0/5 * * 6 6 ?")) // 定义表达式 .build(); // 4: 使用触发器调度任务的执行 scheduler.scheduleJob(job,trigger); // 5:开启 scheduler.start(); //6:关闭 shceduler.shutdown(); } }
- 小提示
“L”和“W”可以一起使用。(企业可用在工资计算,如每个月的几号发工资)
‘#’可表示月中第几周。(可用在计算母亲节、父亲节等)
周字段英文字母不区分大小写,如MON==mon
利用工具在线生成
配置、资源SchedulerFactory
概念
- Quartz以模块方式构架,因此,要使它运行,几个组件必须很好的咬合在一起。幸运的是,已经有了一些现存的助手可以完成这些工作。
- 所有的Scheduler实例都是有SchedulereFactory创建
- Quartz的三个核心概念:调度器、任务、触发器,三者之间的关系是:
- 一个作业,比较重要的三要素就是Scheduler、JobDetail、Trigger;而Trigger对于job而言就好比一个驱动器;没有触发器来定时驱动作业,作业就无法运行,对于job而言,一个job可以对应多个Trigger,但对于Trigger而言,一个Trigger只能对应一个job;所有一个Trigger只能被指定一个job,如果你需要一个复杂的触发器计划,你可以创建多个Trigger并指派给它们同一个job任务
调度器Scheduler创建方式
Quartz默认的SchedulerFactory
方式一:StdSchedulerFactory
- 使用一组参数(java.util.Properties)来创建和初始化Quartz调度器
- 配置参数一般存储在quartz.properties文件中
- 调用getScheduler方法就能创建和初始化调度器对象
SchedulerFactory shcedulerFactory = new SchedulerFactory(); Scheduler scheduler = shcedulerFactory.getScheduler(); // 或者是 Shceduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 或者是 StdSchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = shcedulerFactory.getScheduler();
方式二:DirectSchedulerFactory(了解)
DirectSchedulerFactory是对SchedulerFactory的直接实现,通过它可以直接构建Scheduler、threadpool等
DirectSchedulerFactory directSchedulerFactory = DirectSchedulerFactory.getInstance();
Scheduler shceduler = directSchedulerFactory.getScheduler();
Scheduler使用方式
方法一:输出调度器开始时间
调度器绑定任务和触发器时返回值
Date date = shceduler.shceduleJob(jobDetail,trigger);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("调度器开始时间是:"+dateFormat.format(date));
方法二:启动任务调度
void start();
// 启动调度器,通过触发器执行任务
scheduler.start();
方法三:任务调度挂起(暂停)
void standby();
去停止不同,挂起后可以重新启动
// 启动调度器,通过触发器执行任务
scheduler.start();
// 任务执行两秒
Thread.sleep(2000L);
// Scheduler执行两秒后挂起
scheduler.standby();
// 挂起五秒中时间
Thread.sleep(5000L);
// 挂起调度器五秒后再次启动
scheduler.start();
方法四:关闭任务调度
void shutdown();// 该方法可以无参,还可以携带一个Boolean类型参数
shutdown(true):表示会等待所有正在执行的job任务执行完毕后,再进行关闭Scheduler
shutdown(false):即便任务执行了一半,也会直接关闭scheduler
Quartz.properties
概念
前面我们说到调度器创建,使用的属性便是该文件中指定的属性及值
默认路径:在quartz包中的org.quartz中的quartz.properties
创建调度器时会自动读取加载该文件,可以在SchedulerFactory源码中看到
我们也可以项目资源下添加quartz.properties文件,覆盖底层默认的配置文件,名称固定,会自动覆盖
组成部分
调度器属性
- org.quartz.scheduler.instanceName属性用来区分特定的调度器实例,可以按照功能用途来给调度器取名
- org.quartz.scheduler.instanceId属性和前者一样,也允许任何字符串,但这个值必须在所有调度器实例中是唯一的,尤其是在一个集群环境中,作为集群的唯一key。假如你想Quartz帮你生成这个值的话,可以设置为AUTO,会自动分配唯一的id值
线程池属性
- threadCount属性指处理Job的线程个数,至少为1,但最多的话最好不要超过100,在多数机器上设置该值超过100的话会显示得相当不实用了,特别是你的Job执行时间比较长的情况下
- threadPriority属性指线程的优先级,优先级特别高的线程比级别低的线程优先得到执行。最小为1,最大为10,默认为5
- org.quartz.threadPool.class一个实现了org.quartz.spi.ThreadPool接口的类,Quartz自带的线程池实现类是org.quartz.smpl.SimpleThreadPool
作业存储设置
描述了在调度器实例的生命周期中,Job和Trigger信息是如何被存储的
插件配置
满足特定需求用到的Quartz插件的配置
也可以通过代码操作一些属性
该方式属于硬编码的方式,如果需要修改属性,可能会修改代码,故而不推荐使用,了解即可
public class QuartzProperties {
public static void main(String[] args) {
// 创建工厂实例
StdScheulerFactory factory = new StdSchedulerFactory();
// 创建配置工厂的属性对象
Properties props = new Properties();
// 添加属性,名称及值的方式
props.put("org.quartz.threadPool.threadCount","5");
// 属性名称还可以通过类获取
props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS,"org.quartz.simpl.SimpleThreadPool");
// 使用定义的属性初始化工厂
factory.initialize(props);
Scheduler scheduler = factory.getScheduler();
// 启动测试
scheduler.start();
}
}
Quartz监听器
概念
- Quartz的监听器用于当任务调度中你所关注事件发生时,能够及时获取这一事件的通知。类似于任务执行过程中的邮件、短信类的提醒。
- Quartz监听器主要有JobListener、TriggerListener和SchedulerListener三种,顾名思义,分别表示任务、触发器和调度器对应的监听器。三者的使用方法类似,在开始介绍三种监听器之前,需要明确两个概念:全局监听器与非全局监听器,二者的区别在于:
全局监听器能够接收到所有的Job/Trigger的事件通知
非全局监听器只能接收到在其上注册的Job或Trigger的事件,不在其上注册的Job或Trigger则不会进行监听
JobListener
概念
任务调度过程中,与任务Job相关的事件包括:Job开始要执行的提示;job执行完成的提示等
public interface JobListener {
// 用于获取该JobListener的名称
String getName();
// Scheduler在JobDetail将要被执行时调用这个方法
void jobToBeExecuted(JobExecutionContext var1);
// Scheduler在JobDetail即将被执行,但又被TriggerListener否决时会调用该方法
void jobExecutionVetoed(JobExecutionContext var1);
// Scheduler在JobDetail被执行之后调用这个方法
void jobWasExecuted(JobExecutionContext var1, JobExecutionException var2);
}
示例
创建工作类JobListenerJob
public class JobListenerJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 获取到系统当前的时间
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date = dateFormat.format(new Date());
// 工作内容,打印获取到的时间
System.out.println("工作内容,打印当前时间为:" + date);
}
}
创建自定义Job监听器MyJobListener
public class MyJobListener implements JobListener {
@Override
public String getName() {
// 获取监听器名称,直接返回该监听器对象的类名称,用于测试
return this.getClass().getSimpleName();
}
@Override
public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
// 任务被执行前调用该对象
System.out.println("任务将要被执行");
// 此处可以通过给定参数获取任务信息,如任务名称
String name = jobExecutionContext.getJobDetail().getKey().getName();
System.out.println("任务名称为 " + name);
try {
// 任务一秒后执行
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
// 任务将要被执行但被触发器监听器否决时调用
System.out.println("任务被触发器监听器否决了呀");
}
@Override
public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
// 任务执行结束后执行
System.out.println("任务执行完毕");
}
}
创建调度器执行任务并注册监听(在调度器调度任务触发器之后)
* 调度器调度任务与触发器之后,启动调度器之前注册监听器给任务 * 通过调度器获取到任务监听器管理器 * 再通过管理器添加任务监听器,直接给自定义的监听器对象即可 * 添加监听器时两个参数,参数一为给定要注册的监听器 * 参数二为设置该监听器是全局监听或者局部监听
- 注册全局任务监听器
scheduler.getListenerManager() .addJobListener(new MyJobListener(), EverythingMatcher.allJobs());
- 注册局部任务监听器
scheduler.getListenerManager() .addJobListener(new MyJobListener(), KeyMatcher.keyEquals(JobKey.jobKey("job1","group1")));
public class JobListenerDemo {
public static void main(String[] args) throws SchedulerException {
// 创建调度器,通过默认工厂获取
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 创建任务实例,与JobListener绑定
JobDetail jobDetail = JobBuilder.newJob(JobListenerJob.class)
// 设置任务名称及任务分组
.withIdentity("job1","group1")
.build();
// 创建触发器
Trigger trigger = TriggerBuilder.newTrigger()
// 设置触发器名称及触发器分组
.withIdentity("trigger1","group1")
// 设置触发条件,具体的触发器类型,每两秒触发一次
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(2))
.build();
// 调度任务与触发器
scheduler.scheduleJob(jobDetail,trigger);
// 创建并注册一个全局任务监听器
scheduler.getListenerManager()
.addJobListener(new MyJobListener(), EverythingMatcher.allJobs());
// 创建并注册一个局部任务监听器
scheduler.getListenerManager()
.addJobListener(new MyJobListener(), KeyMatcher.keyEquals(JobKey.jobKey("job1","group1")));
// 启动调度器
scheduler.start();
}
}
TriggerListener
概念
任务调度过程中,与触发器Trigger相关的事件包括触发器触发、触发器未正常触发和触发器完成等
public interface TriggerListener {
// 用于获取触发器的名称
String getName();
// 当与监听器相关联的Trigger被触发,Job上的execute()方法将要被执行时,Scheduler就调用该方法
void triggerFired(Trigger var1, JobExecutionContext var2);
// 在Trigger触发后,Job将要被执行时由Scheduler调用这个方法。TriggerListener给了一个选择去否决Job的执行,假如这个方法返回true,这个Job将不会为此次Trigger触发而得到执行
boolean vetoJobExecution(Trigger var1, JobExecutionContext var2);
// Scheduler调用这个方法是在Trigger错过触发时。你应该关注此方法中持续时间上的逻辑:在出现许多错过触发的Trigger时,长逻辑会导致骨牌效应,你应当保持这上方法尽量的小
void triggerMisfired(Trigger var1);
// Trigger被触发并且完成了Job的执行时,Scheduler调用这个方法
void triggerComplete(Trigger var1, JobExecutionContext var2, CompletedExecutionInstruction var3);
}
示例
创建任务类TriggerListrnerJob
public class TriggerListrnerJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 获取到系统当前的时间
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date = dateFormat.format(new Date());
// 工作内容,打印获取到的时间
System.out.println("工作内容,打印当前时间为:" + date);
}
}
创建自定义Trigger监听器类
public class MyTriggerListener implements TriggerListener {
@Override
public String getName() {
// 获取监听器名称
return this.getClass().getSimpleName();
}
@Override
public void triggerFired(Trigger trigger, JobExecutionContext jobExecutionContext) {
// 触发器触发,任务执行前被调用
System.out.println("触发器被触发,将要执行Job任务了");
}
@Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext jobExecutionContext) {
// true表示触发器否决job执行,false则正常执行
// 该方法在触发后Scheduler正常调用,只是影响Job的执行而已
return false;
}
@Override
public void triggerMisfired(Trigger trigger) {
// 触发器未正常触发执行
System.out.println("触发器没有正常触发,错过触发");
}
@Override
public void triggerComplete(Trigger trigger, JobExecutionContext jobExecutionContext, Trigger.CompletedExecutionInstruction completedExecutionInstruction) {
// 触发器触发,job执行后调用
System.out.println("job执行完毕");
}
}
创建调度器执行任务并注册监听(与任务监听器使用基本类似)
- 注册全局触发器监听器
scheduler.getListenerManager() .addTriggerListener(new MyTriggerListener (), EverythingMatcher.allTriggers());
- 注册局部触发器监听器
scheduler.getListenerManager() .addTriggerListener(new MyTriggerListener(), KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger1","group1")));
public class TriggerListenerDemo {
public static void main(String[] args) throws SchedulerException {
// 创建调度器,通过默认工厂获取
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 创建任务实例,与JobListener绑定
JobDetail jobDetail = JobBuilder.newJob(JobListenerJob.class)
// 设置任务名称及任务分组
.withIdentity("job1","group1")
.build();
// 创建触发器
Trigger trigger = TriggerBuilder.newTrigger()
// 设置触发器名称及触发器分组
.withIdentity("trigger1","group1")
// 设置触发条件,具体的触发器类型,每两秒触发一次
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(2))
.build();
// 调度任务与触发器
scheduler.scheduleJob(jobDetail,trigger);
// 创建并注册一个全局任务监听器
scheduler.getListenerManager()
.addTriggerListener(new MyTriggerListener(),EverythingMatcher.allTriggers());
// 创建并注册一个局部任务监听器
scheduler.getListenerManager()
.addTriggerListener(new MyTriggerListener(), KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger1","group1")));
// 启动调度器
scheduler.start();
}
}
SchedulerListener
概念
SchedulerListener会在Scheduler的生命周期中关键事件发生时被调用。与Scheduler有关的事件包括增加一个Job/trigger、删除一个Job/Trigger、Scheduler发生严重错误和关闭Scheduler等
public interface SchedulerListener {
// 用于部署JobDetail时调用
void jobScheduled(Trigger var1);
// 用于卸载JobDetail时调用
void jobUnscheduled(TriggerKey var1);
// 当一个Trigger来到了再也不会触发的状态时调用这个方法。除非这个Job已经设置成了持久性,否则它就会从Scheduler中移除
void triggerFinalized(Trigger var1);
//
void triggerPaused(TriggerKey var1);
// Scheduler调用这个方法是发生在一个Trigger或Trigger组被暂停时,假如是Trigger组的话,TriggerName参数将为null
void triggersPaused(String var1);
//
void triggerResumed(TriggerKey var1);
// Scheduler调用这个方法是发生成一个Trigger或Trigger组从暂停中恢复时,假如是Trigger组的话,TriggerName参数降为null
void triggersResumed(String var1);
//
void jobAdded(JobDetail var1);
//
void jobDeleted(JobKey var1);
//
void jobPaused(JobKey var1);
// 当一个或一组JobDetail暂停时调用这个方法
void jobsPaused(String var1);
//
void jobResumed(JobKey var1);
// 当一个或一组job从暂停上恢复时调用这个方法,假如一个job组,jobName参数将为空
void jobsResumed(String var1);
// 在Scheduler的正常运行期间产生一个严重错误时调用这个方法
void schedulerError(String var1, SchedulerException var2);
// 当Scheduler处于StandBy模式时,调用该方法
void schedulerInStandbyMode();
// 当Scheduler开启时,调用该方法
void schedulerStarted();
//
void schedulerStarting();
// 当Scheduler停止时,调用该方法
void schedulerShutdown();
//
void schedulerShuttingdown();
// 当Scheduler中的数据被清除时,调用该方法
void schedulingDataCleared();
}
示例
创建任务类SchedulerListrnerJob
public class SchedulerListrnerJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 获取到系统当前的时间
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date = dateFormat.format(new Date());
// 工作内容,打印获取到的时间
System.out.println("工作内容,打印当前时间为:" + date);
}
}
创建自定义Scheduler监听器类
public class MySchedulerListener implements SchedulerListener {
@Override
public void jobScheduled(Trigger trigger) {
System.out.println("部署JobDetail");
}
@Override
public void jobUnscheduled(TriggerKey triggerKey) {
System.out.println("卸载JobDetail");
}
@Override
public void triggerFinalized(Trigger trigger) {
System.out.println("触发器不会再被触发了");
}
@Override
public void triggerPaused(TriggerKey triggerKey) {
System.out.println("触发器被暂停");
}
@Override
public void triggersPaused(String s) {
System.out.println("触发器组被暂停");
}
@Override
public void triggerResumed(TriggerKey triggerKey) {
System.out.println("触发器从暂停中恢复");
}
@Override
public void triggersResumed(String s) {
System.out.println("触发器组从暂停中恢复");
}
@Override
public void jobAdded(JobDetail jobDetail) {
System.out.println("添加任务");
}
@Override
public void jobDeleted(JobKey jobKey) {
System.out.println("删除任务");
}
@Override
public void jobPaused(JobKey jobKey) {
System.out.println("任务被暂停");
}
@Override
public void jobsPaused(String s) {
System.out.println("任务组被暂停");
}
@Override
public void jobResumed(JobKey jobKey) {
System.out.println("暂停的任务被恢复");
}
@Override
public void jobsResumed(String s) {
System.out.println("暂停的任务组被恢复");
}
@Override
public void schedulerError(String s, SchedulerException e) {
System.out.println("触发器触发后发生了严重的错误");
}
@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("调度器数据被清除");
}
}
创建调度器执行任务并注册监听(没有全局局部之分的)
public class TriggerListenerDemo {
public static void main(String[] args) throws SchedulerException {
// 创建调度器,通过默认工厂获取
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 创建任务实例,与JobListener绑定
JobDetail jobDetail = JobBuilder.newJob(JobListenerJob.class)
// 设置任务名称及任务分组
.withIdentity("job1","group1")
.build();
// 创建触发器
Trigger trigger = TriggerBuilder.newTrigger()
// 设置触发器名称及触发器分组
.withIdentity("trigger1","group1")
// 设置触发条件,具体的触发器类型,每两秒触发一次
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(2))
.build();
// 调度任务与触发器
scheduler.scheduleJob(jobDetail,trigger);
// 创建并注册一个调度器监听器
scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener());
// 启动调度器
scheduler.start();
}
}
监听器移除
三种监听器都用的remove方法进行移除
需要注意参数的给定
如移除调度器监听器时,需要给定对应的监听器对象
scheduler.getListenerManager().removeJobListener();
scheduler.getListenerManager().removeTriggerListener();
scheduler.getListenerManager().removeSchedulerListener();