Quartz入门指南
Quartz入门指南
看到官网的教程对于新手来说不够全面和连贯,因此结合自己的使用过程写下这个入门指南,用以解惑。本文基于Quartz2.2.2版本。请注意,本文为了易于上手,省略了许多重要的概念,建议阅读Quartz2.2.x官方教程。
一、安装与配置
下载、解压后,进入lib文件夹,将所有jar文件放入项目指定目录,然后在BuildPath中添加。Jar包共6个,如下所示。
Quartz的运行依赖于log4j.xml和quartz.properties这两个配置文件。关于它们的配置方法,请查阅各自官网。我们偷个懒,就用下载包中的吧。在”quartz-2.2.2-distribution\quartz-2.2.2\examples\example11”目录下找到这俩文件,拷贝到项目指定位置,我是专门新建了个Config文件夹。然后在Eclipse中右击该目录,选择”Build Path -> Use as Source Folder”,即可。
二、Quartz框架
2.1 基本概念
Quartz中最核心的组件是任务、触发器和调度器。任务就是你指定的完成指定业务的执行单元。触发器规定了任务的执行时间、重复周期和频率。调度器将任务和触发器连接起来,它实际上管理着一个线程池和所有的任务和触发器,并进行统一的调度。
2.2 触发器
Quartz包含4种触发器,其中SimpleTrigger 和CronTrigger可能是最常用的,官方文档中对它们进行了详细的解释(请查看Quartz2.2.x官方教程,这里不再详述)。另外两种虽然只在javadoc中有只言片语的介绍,但在特定场景下仍具有不可替代性。
SimpleTrigger :在指定时间激活,然后以指定周期重复指定的次数。比如,14:30分开始,每隔10秒激活一次,重复100次。
CronTrigger:通过一个cron表达式来指定任务激活的年月日星期时分秒,以及重复周期。cron表达式具有一定的语法结构,可以达成非常强大的效果。不受夏令时引起的时钟偏移影响。
CalendarIntervalTrigger 根据一个给定的日历时间进行重复,可以设置启动时间。它可以完成 SimpleTrigger(比如每个月,因为月不是一个确定的秒数)和CronTrigger(比如5个月,因为5个月并不是12个月的公约数)不能完成的一些任务。注意,使用month作为周期单位时,如果起始日期是在某月的最后一天,比如1月31日,那么下一个激活日在2月28日,以后所有的激活日都在当月的28日。如果你要严格限制在每月的最后一天激活,那你需要使用cronTrigger。不受夏令时引起的时钟偏移影响。
DailyTimeIntervalTrigger:在给定的时间窗口或指定的星期以秒、分钟、小时为周期进行重复。比如,每天早上8:00到11:00之间,每隔72分钟激活;或者每周的周一到周五9:20到16:47之间,每隔23分钟激活。注意它的名称Daily,因此重复周期必须在1天以内,不能为星期,月之类的。
2.3 构造器
Quartz提供了构造器风格的API用于构造调度相关的实体。从官方给出的示例代码可以看出,各种实体都是使用构造器来生成的。Quartz中的构造器主要有以下几种:
TriggerBuilder:实例化触发器。
JobBuilder:构造JobDetail。
ScheduleBuilder:构造调度器,前面介绍的4种触发器分别有一个对应的调度器构造器。
DateBuilder:构造一个日期。
2.3.1 TriggerBuilder
通过其成员函数,能够定义触发器的开始、停止时间、job的各种属性及ScheduleBuilder。
具有4个子类,分别是SimpleScheduleBuilder,CronScheduleBuilder,CalendarIntervalScheduleBuilder,DailyTimeIntervalScheduleBuilder,对应4种触发器。在每个子类中,其成员函数均对其特有属性进行配置。比如说,SimpleScheduleBuilder具有repeatForever(),repeatHourlyForTotalCount(int count, int hours) 等方法来设置其重复周期和次数。其余方法不再具体介绍,请查阅javadoc。
2.3.2 JobBuilder
用于构造JobDetail,可想而知,它的成员方法均用于设置JobDetail的各种属性,比如description,dataMap,identity等等。
2.3.3 ScheduleBuilder
具有4个子类,分别为CalendarIntervalScheduleBuilder, CronScheduleBuilder, DailyTimeIntervalScheduleBuilder, SimpleScheduleBuilder,对应4个触发器,分别用于构造具体的某一种触发器。比如说,构造SimpleTrigger时,需要调用SimpleScheduleBuilder的simpleSchedule() 和build()方法;,构造CronTrigger时,需要调用CronScheduleBuilder的cronSchedule(String cronExpression) 和build()方法。其中,build()是由父类继承的,用于生成触发器。
2.3.4 DateBuilder
用于根据各种条件来构造一个日期。比如,dateOf(int hour,int minute, int second, int dayOfMonth, int month, int year),atHourOfDay(int atHour),evenHourDate(Date date),nextGivenMinuteDate(Date date, int minuteBase) ,tomorrowAt(int hour, int minute, int second),validateDayOfMonth(int day) 等等。
三、示例代码
使用SimpleTrigger的代码如下所示:
public void SimpleTriggerTest() throws Exception { SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); JobDetail job1 = newJob(SimpleJob.class).withIdentity("job1", "group1") .build(); Trigger trigger1 = newTrigger()//TriggerBuilder.newTrigger() .startAt(DateBuilder.dateOf(10, 10, 10, 13, 3, 2016)) .withSchedule( simpleSchedule().repeatSecondlyForTotalCount(5, 2))//<SBT extends T> TriggerBuilder<SBT> withSchedule(ScheduleBuilder<SBT> schedBuilder) .build(); Date ft = sched.scheduleJob(job1, trigger1); sched.start(); }
有几个地方需要说明:
1.TriggerBuilder.newTrigger()。这是构造器,由于在头文件中已经使用了”import static org.quartz.TriggerBuilder.newTrigger;”静态引用所以不需要体现类名TriggerBuilder。startAt是它的方法,可以指定触发器的开始时间。
2.withSchedule方法的参数是一个schedBuilder,返回一个TriggerBuilder。
3.repeatSecondlyForTotalCount方法用于设置重复周期和次数,它是由simpleSchedule这个构造器提供的,而不是最终生成的simpleTrigger提供的,simpleTrigger没有相应方法。
完整的示例代码如下:
QuartzDemo.java
import static org.quartz.CalendarIntervalScheduleBuilder.calendarIntervalSchedule; import static org.quartz.CronScheduleBuilder.cronSchedule; import static org.quartz.DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule; import static org.quartz.JobBuilder.newJob; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import static org.quartz.TriggerBuilder.newTrigger; import java.util.Calendar; import java.util.Date; import java.util.HashSet; import java.util.Set; import org.quartz.DateBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.TimeOfDay; import org.quartz.Trigger; import org.quartz.DateBuilder.IntervalUnit; import org.quartz.impl.StdSchedulerFactory; public class QuartzDemo { public void CalendarIntervalTriggerTest() throws SchedulerException{ SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); JobDetail job = newJob(SimpleJob.class).withIdentity("job", "group1").build(); //每隔5个月,在指定日期和时间激活任务 Trigger trigger = newTrigger().startAt(DateBuilder.dateOf(10, 10, 10, 13, 3, 2016)).withSchedule(calendarIntervalSchedule().withInterval(5, IntervalUnit.MONTH)).build(); Date ft = sched.scheduleJob(job, trigger); sched.start(); } public void CronTriggerTest() throws SchedulerException{ SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); JobDetail job = newJob(SimpleJob.class).withIdentity("job2", "group1").build(); //每个周一到周五,早上8点-11点的整点激活任务,从明天早上9点开始 Trigger trigger = newTrigger().startAt(DateBuilder.tomorrowAt(9, 0, 0)).withIdentity("trigger2", "group1").withSchedule(cronSchedule("* 0 8-11 ? * MON-FRI")) .build(); Date ft = sched.scheduleJob(job, trigger); sched.start(); } public void DailyTimeIntervalTriggerTest() throws SchedulerException{ SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); JobDetail job = newJob(SimpleJob.class).withIdentity("job2", "group1").build(); Set<Integer> daysOfWeek = new HashSet<Integer>(); daysOfWeek.add(Calendar.SATURDAY); daysOfWeek.add(Calendar.SUNDAY); //每个周末,20点-20点30分,每隔1分钟激活1次 Trigger trigger = newTrigger().startNow().withSchedule(dailyTimeIntervalSchedule().onDaysOfTheWeek(daysOfWeek).withInterval(1, IntervalUnit.MINUTE).withRepeatCount(5).startingDailyAt(new TimeOfDay(20,00)).endingDailyAt(new TimeOfDay(20,30))) .build(); Date ft2 = sched.scheduleJob(job, trigger); sched.start(); } public void SimpleTriggerTest() throws Exception { SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); JobDetail job = newJob(SimpleJob.class).withIdentity("job1", "group1") .build(); //在指定时间,按照指定周期和次数重复激活 Trigger trigger = newTrigger()//TriggerBuilder.newTrigger() .startAt(DateBuilder.dateOf(10, 10, 10, 13, 3, 2016)) .withSchedule( simpleSchedule().repeatSecondlyForTotalCount(5, 2))//<SBT extends T> TriggerBuilder<SBT> withSchedule(ScheduleBuilder<SBT> schedBuilder) .build(); Date ft = sched.scheduleJob(job, trigger); sched.start(); } public static void main(String[] args) throws Exception { QuartzDemo example = new QuartzDemo(); //example.SimpleTriggerTest(); //example.DailyTimeIntervalTriggerTest(); //example.CalendarIntervalTriggerTest(); example.CronTriggerTest(); } }
SimpleJob.java
import java.util.Date; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class SimpleJob implements Job{ public SimpleJob() { } public void execute(JobExecutionContext context) throws JobExecutionException { System.out.printf("SimpleJob executed at %s%n", new Date()); } }
四、触发器选用实例
到这里,你应该对各种触发器的功能和参数设置方法有了一定的了解,但是仍然建议你看完以下实例,因为有一些你可能没有想到的问题。
4.1 每隔10秒
使用SimpleTrigger 就好。
4.2 每隔90分钟
使用SimpleTrigger或CalendarIntervalTrigger都可以。
4.3 每隔1天
使用CalendarIntervalTrigger 或CronTrigger。不建议使用SimpleTrigger ,因为夏令时的存在可能会使今天14点的24小时以后是明天的13点或者15点。
4.4 每隔2天
使用CalendarIntervalTrigger。不建议使用SimpleTrigger,原因如上。也不建议使用CronTrigger。试想一下,你的CronExpress的“日”这一位可能会写成“2/2”这样,表示每月的2号开始,每个2天。那么在7月的30号激活后,下一次激活是在8月的2号,这个间隔就是3天而不是2天(7月有31天)。
4.5 每隔1周
使用CronTrigger或者CalendarIntervalTrigger都可以。
4.6 每隔2周
与“2天”同样的原因,使用CronTrigger会有问题,因此建议使用CalendarIntervalTrigger。
4.7 每隔1个月
使用CronTrigger或CalendarIntervalTrigger。
4.8 每隔5个月
使用CalendarIntervalTrigger。与“2天”同样的原因,使用CronTrigger会有问题。试想一下,你的CronExpress的“月”这一位可能会写成“3/5”这样,那么8月激活后,下一个激活点在第二年3月,这个间隔就成了7个月。
五、参考资料
Quartz Scheduler Example Programs and Sample Code
Quartz Enterprise Job Scheduler 2.2.1 API