Quartz入门指南
看到官网的教程对于新手来说不够全面和连贯,因此结合自己的使用过程写下这个入门指南,用以解惑。本文基于Quartz2.2.2版本。请注意,本文为了易于上手,省略了许多重要的概念,建议阅读。
一、安装与配置
下载、解压后,进入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可能是最常用的,官方文档中对它们进行了详细的解释(请查看,这里不再详述)。另外两种虽然只在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具有(),(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的() 和build()方法;,构造CronTrigger时,需要调用CronScheduleBuilder的( cronExpression) 和build()方法。其中,build()是由父类继承的,用于生成触发器。
2.3.4 DateBuilder
用于根据各种条件来构造一个日期。比如,dateOf(int hour,int minute, int second, int dayOfMonth, int month, int year),atHourOfDay(int atHour),( date),( date, int minuteBase) ,(int hour, int minute, int second),(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))//TriggerBuilder withSchedule(ScheduleBuilder 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(); SetdaysOfWeek = new HashSet (); 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))// TriggerBuilder withSchedule(ScheduleBuilder 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个月。
五、参考资料