1. Quartz 设计模式
- Bulider模式
- Factory模式
- 组件模式
- 链式编程
2. Quartz两种执行模式
RAMJobStore :
RAMJobStore是默认的数据存储方式,其把数据存在本地内存中,官方宣称这是最有效率的方式(在本地内存当然快啦)。但是在宕机或者重启的时候数据就会丢失。
JDBCJobStore:
JDBCJobStore终于把数据给持久化起来了,这个也是使用最广泛的数据存储方式。在于Spring集成后都不需要自己再配置一遍数据库连接了。建表脚本在官方包里面可以找到(http://www.quartz-scheduler.org/downloads/)
3. Quartz核心概念(通过quartz工厂获取这三个组件)
* 调度器Scheduler
Scheduler将任务Job和触发器Trigger整合起来,负责基于Trigger设定的时间去执行任务Job;
Scheduler作为我们的“任务记录表”,里面(可以)配置大量的Trigger和JobDetail,两者在 Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。
SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler();//将jobDetail和Trigger注册到一个scheduler里,建立起两者的关联关系 scheduler.scheduleJob(jobDetail,Trigger); scheduler.start();//开始任务调度
* 任务Job
job就是你想要实现的任务类,每一个job必须实现org.quartz.job接口,且只需时间接口定义的execu()方法;
public class IapetoseeJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobKey key = context.getJobDetail().getKey(); JobDataMap dataMap = context.getJobDetail().getJobDataMap(); String myString= dataMap.getString("我的字符串"); int myInt = dataMap.getInteger("我的数字"); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); System.out.println("在"+sdf.format(new Date())+"扒取新闻"); } }
* JobDetail补充
Quartz在每次执行任务时,都会创建一个Job实例,并为其配置上下文信息,jobDetail有一个成员属性JobDataMap,存放了我们Job运行时的具体信息,在后面我们会详细提到。
1. 在1.+版本中,它作为一个类,常用的构造方法有:JobDetail(String name, String group, Class jobClass),我们需要指定job的名称,组别和实现了Job接口的自定义任务类。实例如JobDetail jobDetail =new JobDetail("job1", "jgroup1", pickNewsJob.class);
2. 而在2.+版本中,我们则通过一下方法创建 JobBuilder.newJob(自定义任务类).withIdentity(任务名称,组名).build();实例如JobDetail jobDetail = JobBuilder.newJob(pickNewsJob.class).withIdentity(“job1”,”group1”).build();`
JobDetail job = newJob(IapetoseeJob.class) .withIdentity("任务名称", "组名") .usingJobData("我的字符串", "Hello World!").usingJobData("我的数字",12341234) .build();
* 触发器Trigger
Trigger描述了Job执行的时间触发规则,主要有SimpleTrigger和CronTrigger两个子类。
SimpleTrigger simpleTrigger = TriggerBuilde.newTrigger()
.withIdentity("trigger1")//配置触发器名称 .withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(10, 2))//配置重复次数和间隔时间 .startNow()//设置从当前开始 .build();//创建操作
4. 简单实现过程
public static void main(String[] args) throws SchedulerException { //获得调度器 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //创建任务实例 JobDetail jobDetail = JobBuilder.newJob(QiyeJob.class).withIdentity("qiye", "gongshang").build(); //创建任务执行条件 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("qiyeTrigger", "gongshangqiyeTrigger").withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).startNow().build(); //关联任务 scheduler.scheduleJob(jobDetail,trigger); //执行任务 scheduler.start(); }
5. 有状态的job
Quartz,每次执行job,job永远是全新的对象,但是,如果job实现org.quartz.StatefulJob接口,而不是job接口.
StdSchedulerFactory
StdSchedulerFactory是对org.quartz.SchedulerFactory接口的一个实现。是使用一套属性(NameValueCollection)来创建和初始化Quartz Scheduler。这些属性通常在文件中存储和加载。也可以通过编写程序来直接操作工厂。简单地调用工厂的getScheduler()就可以产生一个scheduler,初始化(以及它的ThreadPool、JobStore和DataSources),并且返回一个公共的接口。
DirectSchedulerFactory
DirectSchedulerFactory是SchedulerFactory的另一个实现。它对于那些希望用更加程序化的方式创建Scheduler非常有用。不鼓励使用它的原因如下:
(1) 它需要用户非常了解他们想要干什么。
(2) 它不允许声明式的配置。换句话说,它使用硬编码的方式设置scheduler。
7. Quartz监听器
Quartz的监听器用于当任务调度中你所关注事件发生时,能够及时获取这一事件的通知。类似于任务执行过程中的邮件、短信类的提醒。Quartz监听器主要有JobListener、TriggerListener、SchedulerListener三种,顾名思义,分别表示任务、触发器、调度器对应的监听器。三者的使用方法类似,在开始介绍三种监听器之前,需要明确两个概念:全局监听器与非全局监听器,二者的区别在于:全局监听器能够接收到所有的Job/Trigger的事件通知,而非全局监听器只能接收到在其上注册的Job或Trigger的事件,不在其上注册的Job或Trigger则不会进行监听。
(1)JobListener
a. getName方法:用于获取该JobListener的名称。
b. jobToBeExecuted方法:Scheduler在JobDetail将要被执行时调用这个方法。
c. jobExecutionVetoed方法:Scheduler在JobDetail即将被执行,但又被TriggerListerner否决时会调用该方法
d. jobWasExecuted方法:Scheduler在JobDetail被执行之后调用这个方法
public class SystemJoblistener implements JobListener {
@Override
public String getName() {
//输出当前监听器的名字
return this.getClass().getSimpleName();
}
@Override
public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
//在任务执行之前
String name = jobExecutionContext.getJobDetail().getKey().getName();
System.out.println("1 任务:"+name+"执行了");
}
@Override
public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
//在任务执行被否决的时候
System.out.println("2 Scheduler在JobDetail即将被执行,但又被TriggerListerner否决时会调用该方法");
}
@Override
public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
//在任务执行完成之后执行
String name = jobExecutionContext.getJobDetail().getKey().getName();
System.out.println("3 任务:"+name+"执行完了");
}
}
注册进scheduler中
//设置全局job监听器
ListenerManager listenerManager = scheduler.getListenerManager();
listenerManager.addJobListener(new SystemJoblistener(), EverythingMatcher.allJobs());
(2)TriggerListener
方法 | 说明 |
---|---|
getName() | 定义并返回监听器的名字 |
triggerFired() | 当与监听器相关联的 Trigger 被触发,Job 上的 execute() 方法将要被执行时,Scheduler 就调用这个方法。在全局 TriggerListener 情况下,这个方法为所有 Trigger 被调用。 |
vetoJobExecution() | 在 Trigger 触发后,Job 将要被执行时由 Scheduler 调用这个方法。TriggerListener 给了一个选择去否决 Job 的执行。假如这个方法返回 true,这个 Job 将不会为此次 Trigger 触发而得到执行。 |
triggerMisfired() | Scheduler 调用这个方法是在 Trigger 错过触发时。如这个方法的 JavaDoc 所指出的,你应该关注此方法中持续时间长的逻辑:在出现许多错过触发的 Trigger 时,长逻辑会导致骨牌效应。你应当保持这上方法尽量的小。 |
triggerComplete() | Trigger 被触发并且完成了 Job 的执行时,Scheduler 调用这个方法。这不是说这个 Trigger 将不再触发了,而仅仅是当前 Trigger 的触发(并且紧接着的 Job 执行) 结束时。这个 Trigger 也许还要在将来触发多次的。 |
public class MyTriggerListener implements TriggerListener {
@Override
public String getName() {
return "MyOtherTriggerListener";//这个不能返回null,必须填写;可以使用类的构造器传参的方法传入其他的Trigger监听器名称
}
/**
* (1)
* Trigger被激发 它关联的job即将被运行
* Called by the Scheduler when a Trigger has fired, and it's associated JobDetail is about to be executed.
*/
@Override
public void triggerFired(Trigger trigger, JobExecutionContext context) {
System.out.println("MyOtherTriggerListener.triggerFired()");
}
/**
* (2)
* Trigger被激发 它关联的job即将被运行,先执行(1),在执行(2) 如果返回TRUE 那么任务job会被终止
* Called by the Scheduler when a Trigger has fired, and it's associated JobDetail is about to be executed
*/
@Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
System.out.println("MyOtherTriggerListener.vetoJobExecution()");
return false;
}
/**
* (3) 当Trigger错过被激发时执行,比如当前时间有很多触发器都需要执行,但是线程池中的有效线程都在工作,
* 那么有的触发器就有可能超时,错过这一轮的触发。
* Called by the Scheduler when a Trigger has misfired.
*/
@Override
public void triggerMisfired(Trigger trigger) {
System.out.println("MyOtherTriggerListener.triggerMisfired()");
}
/**
* (4) 任务完成时触发
* Called by the Scheduler when a Trigger has fired, it's associated JobDetail has been executed
* and it's triggered(xx) method has been called.
*/
@Override
public void triggerComplete(Trigger trigger, JobExecutionContext context,
CompletedExecutionInstruction triggerInstructionCode) {
System.out.println("MyOtherTriggerListener.triggerComplete()");
}
}
//向调度程序注册全局监听触发器
scheduler.getListenerManager().addTriggerListener(myTriggerListener, allTriggers());
//向调度程序注册一个指定的监听触发器
scheduler.getListenerManager().addTriggerListener(myTriggerListener, triggerKeyEquals(triggerKey("myTriggerName", "myTriggerGroup")));
//向调度程序注册一个特定组的触发器
scheduler.getListenerManager().addTriggerListener(myTriggerListener, triggerGroupEquals("myTriggerGroup"));
(3)SchedulerListener