Quartz
核心概念
- 任务job
job就是你想要实现的任务类,每一个job必须实现org.quartz.job接口,且只需要实现接口定义的execute()方法
- 触发器Trigger
Trigger为你执行任务的触发器,比如你每天定时3点发送一份统计邮件,Trigger将会设置3点执行该任务。Trigger主要包括Simple Trigger和CronTrigger两种。
- 调度器Scheduler
调度器它将任务job及触发器Trigger整合起来,负责基于Trigger设定的时间来执行job
Quartz的常用的几个API
-
Scheduler用于与调度程序交互的主程序接口。
Scheduler调度程序-任务执行计划表,只有安排进执行计划的任务job(通过scheduler.scheduleJob方法安排进执行计划),当它到了预先定义的执行时间
该任务才会启动
-
Job我们预先定义的希望在未来定时任务的实例,我们可以自定义
-
JobDetail 使用JobDetail 来定义定时任务的实例,该实例是通过JobBuilder类创建的
-
JobDataMap:可以包含不限量的数据对象,在Job实例执行的时候,可以使用其中的数据,JobDataMap是Java Map接口的一个实现,额外增加了一些存储基本数据类型的数据方法
-
Trigger触发器:Trigger对象是用来出发执行Job的,当调度一个Job时,我们实例一个出发去然后调整它的属性来满足Job执行的条件,表明任务在什么时候执行,定义了一个已经被安排的任务会在什么时候执行的时间条件:比两秒一次
-
JobBuilder:用于声明一个任务实例,也可以定义关于该任务的详情比如任务名,组名等,这个声明的实例将会作为一个实例执行的任务
-
TriggerBuilder:触发器的创建器,用于创建触发器trigger实例
-
JobListener、TriggerListener、SchedulerListener监听器,用于对组件的监听
入门案例
- 首先有一个java类 也就是需要实现的任务
public class Hellojob implements Job {
public void execute(JobExecutionContext context){
//输出当前时间
Date date=new Date();
SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = dateFormat.format(date);
//工作内容
System.out.println("时间是"+format);
}
}
- 创建任务调度类
public class HelloSchedulerDemo {
public static void main(String[] args) throws SchedulerException {
//调度器 Scheduler 获取实例调度
Scheduler fact = StdSchedulerFactory.getDefaultScheduler();
//任务实例JobBulider
JobDetail jobDetail = JobBuilder.newJob(Hellojob.class)
.withIdentity("job1", "group1")//参数一 任务名称 唯一实例 参数二 任务组名称
.build();
//触发器 Trigger
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")//参数一 任务名称 唯一实例 参数二 任务组名称
.startNow()//马上启动
.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5))//设置每5秒执行一次
.build();
//让调度器去关联触发器 保证触发器定义的条件执行任务
fact.scheduleJob(jobDetail,trigger);
fact.start();
}
}
Job与JobDetail介绍
-
Job:工作任务调度的接口,任务类需要实现该接口,该接口中定义execute方法,类似JDK提供的TimeTask类的run方法,在里面编写业务逻辑
-
Job实例在Quartz中的生命周期,每次调度器执行Job时,它在调用execute方法前会创建一个新的Job实例,当调度完成后,关联的Job对象实例会完全释放,释放的实例会被垃圾回收机制回收
-
JobDetail:JobDetail为Job实录提供了许多属性,以及JobDateMap成员变量属性,它用来存储特定job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例
-
public static void main(String[] args) throws SchedulerException { //调度器 Scheduler 获取实例调度 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //任务实例JobBulider JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1","group1") .usingJobData("message","打印日志") .build(); System.out.println(jobDetail.getKey().getGroup()); System.out.println(jobDetail.getKey().getName()); System.out.println(jobDetail.getKey().getClass());
JobExecutionContext介绍
-
当Scheduler调用一个job,就会将JonExecutionContext传递给Job的execute()方法
-
Job能通过JobExecutionContext对象访问到Quartz运行时候的环境以及job本身的明细数据
JobDataMap介绍
-
使用Map获取
-
在进行任务调度时,jobDataMap存储在JobExecutionContext中,非常方便获取
-
public class HelloScheduleDemo { public static void main(String[] args) throws SchedulerException { //调度器 Scheduler 获取实例调度 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //任务实例JobBulider JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1","group1") .usingJobData("message","打印日志") //添加信息 .build(); System.out.println(jobDetail.getKey().getGroup()); System.out.println(jobDetail.getKey().getName()); System.out.println(jobDetail.getKey().getClass()); //触发器Trigger Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1","group1") .startNow() .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)) .usingJobData("message","simple触发器") //添加信息 .build(); //让调度器去关联触发器,保证触发器定义的条件执行任务 scheduler.scheduleJob(jobDetail,trigger); scheduler.start(); } }
-
可以在HelloJob中获取
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss");
String format = dateFormat.format(date);
JobDetail jobDetail = jobExecutionContext.getJobDetail();
//从JobDetail中获取JobDataMap的数据
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
System.out.println(jobDataMap.getString("message"));
//从Trigger中获取JobDataMap的数据
JobDataMap jobDataMap1 = jobExecutionContext.getTrigger().getJobDataMap();
System.out.println(jobDataMap1.getString("message"));
System.out.println("时间是:"+format);
}
}
也可以用setter方法来获取trigger或者JobBulider中的值
但是如果trigger与JobBulider都有一个相同的变量那只会输出trigger中的值
private String message;
public void setMessage(String message) {
this.message = message;
}
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss");
String format = dateFormat.format(date);
JobDetail jobDetail = jobExecutionContext.getJobDetail();
//从JobDetail中获取JobDataMap的数据
// JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
// System.out.println(jobDataMap.getString("message"));
//从Trigger中获取JobDataMap的数据
// JobDataMap jobDataMap1 = jobExecutionContext.getTrigger().getJobDataMap();
// System.out.println(jobDataMap1.getString("message"));
//从set方法中读出
System.out.println(message);//输出simple触发器
System.out.println("时间是:"+format);
}
有状态的Job和无状态的Job
-
有状态的Job可以理解为多次Job调用期间可以持有一些状态信息,这些状态信息存储在JobDataMap中,而默认的无状态的Job每次调用是会创建一个新的JobDataMap
-
@PersistJobDataAfterExecution//多次调用Job的时候,会对Job进行持久化,即保存一个数据的信息
-
在配置类中传入一个参数count,初始值为0
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) .withIdentity("job1","group1") .usingJobData("message","打印日志") .usingJobData("count",0) .build();
-
在Job类中加上注解@PersistJobDataAfterExecution,使用set方法得到count
private int count;
public void setCount(int count) {
this.count = count;
}
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
++count;
System.out.println(count);
jobExecutionContext.getJobDetail().getJobDataMap().put("count",count);//将数据放到JobDataMap里面、这样每次的count都会递增
}
Trigger介绍
- Jobkey:表示Job实例的标识,触发器被触发时,该指定的job实例会被执行
- startTime:设置触发器第一次被触发的时间
- endTime:设置触发器被终止触发的时间
SimpleTrigger触发器
- SimpleTrigger对于设置和使用手机一种最为简单的QuartzTrigger
- 它是为那种需要在特定的日期/时间启动且以一个可能的间隙时间重复执行n次的Job所设计
//触发器Trigger
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1","group1")
.startAt(startDate)//设置任务的开始时间
.endAt(endDate) #设置任务结束时间 .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)//每5秒执行一次,只执行4次 从0开始
.withRepeatCount(3))
.build();
- 如果指定的结束时间与指定的次数相互冲突,则结束时间的属性优先级是优于重复属性
CronTrigger触发器
-
如果你需要像日历那样的按日程来触发任务,而不是像SimpleTrigger那样每隔特定的时间间隔触发,CronTrigger比SimpleTrigger更有用
-
使用CronTrigger,你可以指定诸如“每个周五中午”,或者“每个工作日的9:30”
这样的日程安排来触发
-
案例
//触发器Trigger
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1","group1")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0-30 * * * * ?"))//设置你想要触发的时间 如上表的案例
.usingJobData("message","simple触发器")
.build();
Scheduler
-
创建
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
-
scheduler.scheduleJob(jobDetail, trigger);//让调度器去关联触发器,保证触发器定义的条件执行任务 scheduler.start(); //启动 scheduler.standby();//挂起 scheduler.shutdown(true);//关闭,但是在所有的Job任务结束后才关闭 scheduler.shutdown(false);//关闭,直接关闭,不会等待Job任务的结束
监听器
- Quartz的监听器用于当任务调度中你所关注时间发生时,能够及时获取这一事件的通知。类似于任务执行过程中的邮件、短信类的提醒。Quartz的监听器主要有JobListener、TriggerListener、SchedulerListener三种,顾名思义,分别表示任务、触发器、调度器对应的监听器。三者的使用方法类似,在开始介绍三种监听器,需要明确两个概念:全局监听器和非全局监听器的区别:
- 全局监听能够接收收到所有的Job/Triggerd的时间通知
- 非全局监听器只能接收到在其上注册的Job或者Trigger的事件,不在其上注册的Job或者Trigger不会进行监听
JobListener
-
任务调度过程中:与任务Job相关的事件包括:Job开始要执行的提示、Job执行完成的提示
-
创建一个MyListener配置类
public class MyJobListener implements JobListener { public String getName() { //监听器的名称 String simpleName = this.getClass().getSimpleName(); System.out.println("监听器的名称+ "+simpleName); return simpleName; } public void jobToBeExecuted(JobExecutionContext jobExecutionContext) { String name = jobExecutionContext.getJobDetail().getKey().getName(); System.out.println("Scheduler在JobDetial将要被执行时调用的方法 job名称"+name); } public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) { String name = jobExecutionContext.getJobDetail().getKey().getName(); System.out.println("Scheduler在JobDetail即将被执行,但又被TriggerListener否决时会调用该方法 job,名称"+name); } public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) { String name = jobExecutionContext.getJobDetail().getKey().getName(); System.out.println("Scheduler在JobDetail被执行之后调用这个方法 job名称 "+name); } }
在HelloListenerDemo中注册监听
public class HelloJobListenerDemo {
public static void main(String[] args) throws SchedulerException {
//调度器 Scheduler 获取实例调度
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//任务实例JobBulider
JobDetail jobDetail = JobBuilder.newJob(HelloListener.class)
.withIdentity("job1","group1")
.usingJobData("message","打印日志")
.usingJobData("count",0)
.build();
System.out.println(jobDetail.getKey().getGroup());
System.out.println(jobDetail.getKey().getName());
System.out.println(jobDetail.getKey().getClass());
//触发器Trigger
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1","group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
.usingJobData("message","simple触发器")
.build();
//让调度器去关联触发器,保证触发器定义的条件执行任务
scheduler.scheduleJob(jobDetail,trigger);
//创建并注册一个全局的Job Listener
// scheduler.getListenerManager().addJobListener(new MyJobListener(), EverythingMatcher.allJobs());
//创建一个局部的Job Listener
scheduler.getListenerManager().addJobListener(new MyJobListener(), KeyMatcher.keyEquals(JobKey.jobKey("job1","group1")));//这两个参数就是上面jobBulider中设置的withIdentity("job1","group1")中的两个参数
scheduler.start();
}
}
TriggerListener
-
任务调度中,与触发器Trigger相关的事件包括:触发器触发、触发器未正常触发、触发器完成
-
创建一个MyTriggerListener
public String getName() {
String name = this.getClass().getName();
System.out.println("触发器的名称:"+name);
return name;
}
public void triggerFired(Trigger trigger, JobExecutionContext jobExecutionContext) {
//当与监听器相关联的Trigger被触发、job上的execute()方法被执行时,Scheduler就调用该方法
String name = trigger.getKey().getName();
System.out.println(name+" 这个触发器触发");
}
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext jobExecutionContext) {
//在Trigger触发后,Job将要执行时,由Scheduler调用这个方法,TriggerListener给了一个选择去否决Job的执行
//假如这个方法返回True,这个Job就不会为此次Trigger触发而得到执行
String name = trigger.getKey().getName();
System.out.println(name+" 没有被触发器触发");
return false;
}
public void triggerMisfired(Trigger trigger) {
/*Scheduler调用这个方法是在错过触发时,你应该关注此方法中持续时间长的逻辑*/
String name = trigger.getKey().getName();
System.out.println(name+" 错过触发");
}
public void triggerComplete(Trigger trigger, JobExecutionContext jobExecutionContext, Trigger.CompletedExecutionInstruction completedExecutionInstruction) {
/*Trigger被触发并且完成了Job的执行时,Scheduler调用这个方法*/
String name = trigger.getKey().getName();
System.out.println(name+" 完成触发");
}
}
在HelloTriggerListener注册监听
public class HelloTriggerListener {
public static void main(String[] args) throws SchedulerException {
//调度器 Scheduler 获取实例调度
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//任务实例JobBulider
JobDetail jobDetail = JobBuilder.newJob(HelloTrigger.class)
.withIdentity("job1","group1")
.usingJobData("message","打印日志")
.usingJobData("count",0)
.build();
//触发器Trigger
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1","group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))
.usingJobData("message","simple触发器")
.build();
//让调度器去关联触发器,保证触发器定义的条件执行任务
scheduler.scheduleJob(jobDetail,trigger);
//创建并注册一个全局的Trigger Listener
scheduler.getListenerManager().addTriggerListener(new MyTriggerListener(), EverythingMatcher.allTriggers());
// //创建并注册一个局部的Trigger Listener
//scheduler.getListenerManager().addTriggerListener(new MyTriggerListener(),KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger","group1")));
scheduler.start();
}
}
SchedulerListener
-
SchedulerListener会在Scheduler的生命周期中关键事件发生时被调用,与Scheduler有关的事件:增加一个Job/trigger、删除一个Job/trigger、scheduler发生错误、关闭scheduler等
public class MySchedulerListener implements SchedulerListener {
public void jobScheduled(Trigger trigger) { String name = trigger.getKey().getName();
// 部署JobDetail是调用
System.out.println(name+ " 完成部署");
}public void jobUnscheduled(TriggerKey triggerKey) { String name = triggerKey.getName();
// 卸载JobDetail是调用
System.out.println(name+ " 完成卸载");
}public void triggerFinalized(Trigger trigger) { String name = trigger.getKey().getName(); System.out.println(name+" 被移除"); } public void triggerPaused(TriggerKey triggerKey) { String name = triggerKey.getName(); System.out.println(name+ " 正在被暂停"); } public void triggersPaused(String s) { System.out.println(s+"组 正在被暂停"); } public void triggerResumed(TriggerKey triggerKey) { String name = triggerKey.getName(); System.out.println(name+ "正在从暂停中恢复"); } public void triggersResumed(String s) { System.out.println(s+ "组 正在从暂停中恢复"); } public void jobAdded(JobDetail jobDetail) { System.out.println(jobDetail.getKey().getName()+"添加工作任务"); } public void jobDeleted(JobKey jobKey) { System.out.println(jobKey.getName() +"添加工作任务"); } public void jobPaused(JobKey jobKey) { System.out.println(jobKey.getName() +"暂停工作任务"); } public void jobsPaused(String s) { System.out.println(s+" 组 暂停工作任务"); } public void jobResumed(JobKey jobKey) { System.out.println(jobKey.getName() +"恢复工作任务"); } public void jobsResumed(String s) { System.out.println(s+" 组 恢复工作任务"); } public void schedulerError(String s, SchedulerException e) { } public void schedulerInStandbyMode() { } public void schedulerStarted() { } public void schedulerStarting() { } public void schedulerShutdown() { } public void schedulerShuttingdown() { } public void schedulingDataCleared() { }
}
-
public class HelloSchedulerListenerDemo { public static void main(String[] args) throws SchedulerException { Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); Date startDate = new Date(); //任务实例JobBulider JobDetail jobDetail = JobBuilder.newJob(HelloJobSimple.class) .withIdentity("job1","group1") .usingJobData("message","打印日志") .build(); //触发器Trigger Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1","group1") .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5))//每5秒执行一次,只执行4次从 0开始 .build(); //让调度器去关联触发器,保证触发器定义的条件执行任务 scheduler.scheduleJob(jobDetail,trigger); // 创建调度器监听 scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener()); scheduler.start(); } }