Quartz技术
1.触发器(Triggers)
与Job相比,Trigger相对来说比较简单容易,但是要能完全的掌握使用Quartz,使其包含各种自定义的时间表选项,我们必须先知道和理解Trigger。
1.1日历(Calendars)
Quartz Calendar 对象(不是java.util.Calendar对象)能够与被注册进Scheduler的Trigger关联。Calendar对排除Trigger的 时间段是很有用的,例如,我们可以创建一个在每个工作日上午9:30触发Job的Trigger,就在其中增加一个排除所有工作假期的Calendar。
Calendar可以是任何实现Calendar接口的可序列化对象,如下所示:
package org.quartz;
public interface Calendar {
public boolean isTimeIncluded(long timeStamp);
public long getNextIncludedTime(long timeStamp);
}
该接口的参数都是long类型的,以毫秒为单位的timestamp;所以Calendar定义的时间可以准确到毫秒。很可能,我们需要排除一整天,为了方便,Quartz包含了实现该功能的类ori.quartz.impl.HolidayCalendar。
Calendar必须实例化并且通过addCalendar(…)方法注册到Scheduler中,如果使用HolidayCalendar,在实例化之后,我们需要使用方法addExcludedDate(Date date)排除我们计划日程中不需要的日期,一个Calendar实例可以用到不同的Trigger中,比如:
HolidayCalendar cal = new HolidayCalendar();
cal.addExcludedDate( someDate );
sched.addCalendar("myHolidays", cal, false);
SimpleTrigger trigger = new SimpleTrigger("myTrigger",
sched.DEFAULT_GROUP,
new Date(),
null,
SimpleTrigger.REPEAT_INDEFINITELY,
60L * 1000L);
trigger.setCalendarName("myHolidays");
// .. schedule job with trigger
SimpleTrigger trigger2 = new SimpleTrigger("myTrigger",
sched.DEFAULT_GROUP,
new Date(),
null,
5,
5L * 24L * 60L * 60L * 1000L);
trigger2.setCalendarName("myHolidays");
// .. schedule job with trigger2
上面代码,我们创建了两个Trigger:一个是每1分钟执行触发一次,没有次数限制;另一个是每5天执行触发一次,共执行5次触发;然而,任何在Calendar中被排除的时间段的触发执行都将被取消。
1.2 过时触发指令(Misfire Instructions)
Trigger另一个重要的属性是“Misfire Instruction”。过时触发发生在持久Trigger失去了触发时间,由于对应的Scheduler被关闭的原因;不同的Trigger类型有不 同的过时触发指令,默认情况下是使用“smart policy”指令,该指令根据Trigger类型和配置具有动态的行为。当scheduler启动时,它将搜索所有过时触发的持久Trigger,并同 时根据它们各自所配置的过时触发指令进行更新;当我们在项目中要使用Quartz时,我们必须熟悉所要使用的Trigger类型的过时触发指示,在对应的 JavaDOC中有对其进行说明。可以通过方法setMisfireInstruction(…)来设置Trigger的过时触发指示。
1.3 触发器的辅助类(TriggerUtils)
TriggerUtils类(在org.quartz.helpers包中)为我们创建Trigger和触发时间提供了方便,可以使我们不用 java.util.Calendar对象。使用辅助类能够很容易地创建基于每分钟、小时、天、周和月等触发的触发器,也可以创建各种各样的日期――对设 置触发器的启动时间很有用。
1.4触发器的监听器(TriggerListeners)
Trigger能够像Job一样,可以把监听器注册到Trigger中,实现了接口TriggerListener的对象就可以接收到Trigger触发时的通知。
2.Simple触发器(SimpleTrigger)
当我们需要在规定的时间执行一次或在规定的时间段以一定的时间间隔重复触发执行Job时,SimpleTrigger就可以满足上述要求。
SimpleTrigger的属性有:开始时间、结束时间、重复次数和重复的时间间隔,重复次数属性的值可以为0、正整数、或常量 SimpleTrigger.REPEAT_INDEFINITELY,重复的时间间隔属性值必须为0或长整型的正整数,以毫秒作为时间单位,当重复的时 间间隔为0时,意味着与Trigger同时触发执行(或几乎与Scheduler开始时同时触发执行)。
如果有指定结束时间属性值,则结束时间属性优先于重复次数属性,这样的好处在于:当我们需要创建一个每间隔10秒钟触发一次直到指定的结束时间的 Trigger,而无需去计算从开始到结束的所重复的次数,我们只需简单的指定结束时间和使用REPEAT_INDEFINITELY作为重复次数的属性 值即可(我们也可以指定一个比在指定结束时间到达时实际执行次数大的重复次数)。
SimpleTrigger有几个不同的构造方法,我们只对下面这个进行分析:
public SimpleTrigger(String name, String group, Date startTime, Date endTime, int repeatCount, long repeatInterval)
SimpleTrigger例1――创建一个在当前之后10秒钟触发的,执行一次的Trigger
long startTime = System.currentTimeMillis() + 10000L;
SimpleTrigger trigger = new SimpleTrigger("myTrigger",
sched.DEFAULT_GROUP,
new Date(startTime),
null,
0,
0L);
SimpleTrigger例2――创建一个立即触发的,并每间隔60秒钟重复触发执行一次的Trigger
SimpleTrigger trigger = new SimpleTrigger(”myTrigger”,
sched.DEFAULT_GROUP,
new Date(),
null,
SimpleTrigger.REPEAT_INDEFINITELY,
60L * 1000L);
SimpleTrigger例3――创建一个立即触发,每间隔10秒钟重复触发执行一次,开始时间为当前,结束时间为40秒钟后的Trigger
long endTime = System.currentTimeMillis() + 40000L;
SimpleTrigger trigger = new SimpleTrigger("myTrigger",
sched.DEFAULT_GROUP,
new Date(),
new Date(endTime),
SimpleTrigger.REPEAT_INDEFINITELY,
10L * 1000L);
SimpleTrigger例4――创建一个在2005年5月8日早上10:30触发的,每间隔30秒钟重复触发执行一次,并且重复执行5次(总共触发执行6次)的Trigger
java.util.Calendar cal = new java.util.GregorianCalendar(2005, cal.MAY, 8);
cal.set(cal.HOUR, 10);
cal.set(cal.MINUTE, 30);
cal.set(cal.SECOND, 0);
cal.set(cal.MILLISECOND, 0);
Data startTime = cal.getTime()
SimpleTrigger trigger = new SimpleTrigger("myTrigger",
sched.DEFAULT_GROUP,
startTime,
null,
5,
30L * 1000L);
2.1 Simple触发器的过时触发指令(SimpleTrigger Misfire Instructions)
SimpleTrigger有几个用于当过时触发发生时向Quartz通知如何执行的指令,这些指令作为常量定义在SimpleTrigger类中,分别如下:
MISFIRE_INSTRUCTION_FIRE_NOW
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
我们也可以使用前面讨论的Trigger.MISFIRE_INSTRUCTION_SMART_POLICY,该指令是所有Trigger的默认值。
如果使用“smart policy”,SimpleTrigger将会根据实例中的配置进行动态的选择过时触发指令,JavaDOC中的SimpleTrigger.updateAfterMisfire()方法详细解析了动态选择的内容。
3.Cron触发器(CronTrigger)
CronTrigger的功能比SimpleTrigger强大,它可以实现基于类似于日历概念的作业调度,而不是单单基于一定的时间间隔的。
使用CronTrigger,我们能够定义如下类型的日历型的Schedule:每个星期五的中午,每个周末的早上9:30,或者每周一、周三和周五的早上9:00到10:00每隔5分钟。
3.1 Cron表达式(Cron Expressions)
Cron-Expressions用于配置CronTrigger实例,Cron-Expressions是一串字符串,实际上它由六个子字符串组成,子字符串间采用空格分离,从左到右分别代表:Seconds Minutes Hours Day-of-Month Month Day-of-Week。
字符串“0 0 12 ? * WED”是一个完整的Cron-Expressions例子,它所表达的意思是:每周三早上12:00。
各个子表达式的值可以是一个范围或者列表,比如,上个例子中的Day-of-Week域的值“WED”可以用“MON-FRI”、“MON,WED,FRI”或者“MON-WED,SAT”来替代。
所有子表达式都有指定各自的取值范围,下面对Cron-Expressions的各个子表达式和取值范围进行说明:
子表达式 |
允许的值 |
允许的特殊字符 |
Seconds |
0-59 |
- * / |
Minutes |
0-59 |
- * / |
Hours |
0-23 |
- * / |
Day-of-Month |
1-31 |
- * ? / L W |
Month |
1-12或JAN-DEC |
- * / |
Day-of-Week |
1-7或SUN-SAT |
- * ? / L # |
Years(Optional) |
为空或1970-2099 |
- * / |
‘-’字符表示:值的范围,10-12在Hours域中表示为:10、11和12;
‘*’字符表示:可以为任意值,‘*’在Minutes域中表示为:每分钟;
‘/’字符表示:一个左边的数值是右边基数的递增值,‘0/15’在Seconds域中表示为:第0、15、30和45秒,‘5/15’ 在Seconds域中表示为:第5、20、35和50;
‘?’字符表示:没有对该域指定值,可以为任意值,不对该域进行限制,‘?’只能用在Day-of-Month和Day-of-Week域中;
‘L’字符表示:‘L’是取‘Last’的第一个字母,也只能用在Day-of-Month和Day-of-Week域中,在Day-of-Month域 中的意思是:月份的最后一天,在Day-of-Week域中的意思是:‘7’或‘SAT’,但是在Day-of-Week域中用在另一个值后的意思是:该 月的最后一个星期几,如:‘6L’为:该月的最后一个星期五;
‘W’字符表示:‘W’是取‘Weekday’的第一个字母,只能用在Day-of-Month域中,‘W’代表了最接近给定那一天的工作日,如:在 Day-of-Month域中把值设为:15W,表示的意思是:该月的第15日最接近的工作日,所以当第15日是周六时,Trigger将在第14日的周 五执行触发,当第15日是周天时,Trigger将在第16日的周一执行触发,当第15日是工作日时,Trigger将在第15日的当天执行触发;然而在 Day-of-Month域中把值设为:1W,当第1日是周六时,Trigger将在第3日的周一执行触发,因为它不会跨越月份的范围,‘W’字符只能是 该月中的一天,而不是一个范围或天数的列表;在Day-of-Month 域中可以结合使用‘L’和‘W’字符‘LW’,‘LW’表示:月份的最后一个工作日;
‘#’字符表示:月份的第几个星期几,只能用在Day-of-Week域中,如:在Day-of-Week域中把值设为:6#3,表示的意思是:该月的第3个星期5(day 6=Friday and “#3”=the 3rd one in the month);2#1表示的意思是:该月的第1个星期1;4#5表示的意思是:该月的第5个星期3;需要注意:不能把值设为“#5”,因为一个月中不可能有第5个星期3,如果这样设置,将导致该月无法执行触发;
下面举一些完整的Cron- Expressions例子:
表达式 |
意思 |
0 0 12 * * ? | 每天12:00执行触发 |
0 15 10 ? * * | 每天10:15执行触发 |
0 15 10 * * ? | 每天10:15执行触发 |
0 15 10 * * ? * | 每天10:15执行触发 |
0 15 10 * * ? 2005 | 2005年的每天10:15执行触发 |
0 * 14 * * ? | 每天从14:00到14:59每隔1分钟执行一次触发 |
0 0/5 14 * * ? | 每天从14:00到14:59每个5分钟执行一次触发 |
0 0/5 14,18 * * ? | 每天从14:00到14:59和18:00到18:59每隔5分钟执行一次触发 |
0 0-5 14 * * ? | 每天从14:00到14:05每隔1分钟执行一次触发 |
0 10,44 14 ? 3 WED | 3月的每个星期3的14:10和14:44分别执行一次触发 |
0 15 10 15 * ? | 每月的第15日10:15执行一次触发 |
0 15 10 L * ? | 每月最后一天的10:15执行一次触发 |
0 15 10 ? * 6L | 每月的最后一个星期5的10:15执行一次触发 |
0 15 10 ? * 2002-2005 | 2002、2003、2004、2005年的每个月的最后一个星期5的10:15执行一次触发 |
0 15 10 ? * 6#3 | 每月的第3个星期5的10:15执行一次触发 |
当Schedule中要求比较复杂,采用单个的Trigger无法实现时,可以配置多个的Trigger来实现,比如:每天的9:00到10:00之间每 隔5分钟执行一次触发,并且在每天的13:00到22:00之间每隔20分钟执行一次触发,配置2个Trigger并把2配置好的2个Trigger注册 到相同的Job中就可以很简单的实现上述情况。
Quartz中SimpleTrigger的探讨
1.来写一个每隔10秒启动一次任务的例子.
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
public class SimpleTriggerTest
{
public static void main(String[] args) throws Exception
{
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
// 系统当前时间10秒后
long startTime = System.currentTimeMillis() + 10000L;
SimpleTrigger trigger = new SimpleTrigger("myTrigger", null, new Date(
startTime), null, 0, 0L);
JobDetail jobDetail = new JobDetail();
jobDetail.setJobClass(StringTest.class);
jobDetail.setName("test");
jobDetail.setGroup("A");
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
}
2.创建一个触发器,立即启动,每隔60秒,启动一次.
null ,
new Date(),
null ,
SimpleTrigger.REPEAT_INDEFINITELY,
60L * 1000L );
SimpleTrigger trigger = new SimpleTrigger( " myTrigger " ,
" myGroup " ,
new Date(),
new Date(endTime),
SimpleTrigger.REPEAT_INDEFINITELY,
10L * 1000L );
cal.set(cal.HOUR, 10 );
cal.set(cal.MINUTE, 30 );
cal.set(cal.SECOND, 0 );
cal.set(cal.MILLISECOND, 0 );
Data startTime = cal.getTime()
SimpleTrigger trigger = new SimpleTrigger( " myTrigger " ,
null ,
startTime,
null ,
5 ,
30L * 1000L );
仔细体会SimpleTrigger的例子,你会发现用这个制作执行计划会更简单方便.
一个小例子,
DelayTest class
---------------
package test.com.bsmart.bmc.operator.taxi;
import java.util.Date;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
public class DelayTest {
public void addDelayJob(){
SchedulerFactory scheFact = new StdSchedulerFactory();
Scheduler sche = null;
try {
sche = scheFact.getScheduler();
sche.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
JobDetail jobDetail = new JobDetail("myJob","job1",JobTest.class);
// JobTest jobTest = new JobTest();
SimpleTrigger trigger = new SimpleTrigger("test","testGroup",new Date(new Date().getTime()+5*1000),null,5,10*1000);
try {
sche.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
public static void main(String [] args){
DelayTest test = new DelayTest();
test.addDelayJob();
}
}
job class
-------------------------------------------------
package test.com.bsmart.bmc.operator.taxi;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
public class JobTest implements Job{
// private SimpleTrigger trigger;
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("********************************8");
System.out.println("********************************8");
System.out.println("********** job test *******8");
System.out.println("********************************8");
System.out.println("********************************8");
System.out.println("name="+arg0.getJobDetail().getName());
System.out.println("fullname="+arg0.getJobDetail().getFullName());
System.out.println("description="+arg0.getJobDetail().getDescription());
System.out.println();
System.out.println("********************************8");
System.out.println("*********** delete the job *********8");
SchedulerFactory scheFact = new StdSchedulerFactory();
Scheduler sche = null;
boolean tag = false;
try {
sche = scheFact.getScheduler();
// sche.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
// try {
// tag = sche.deleteJob("myJob","job1");
// } catch (SchedulerException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
if(tag){
System.out.println("delete job sucess");
}else{
System.out.println("delete job failed");
}
}
}