最近使用到Quartz框架来做定时推送数据的功能的调度(注:在Java可以使用java.util.Timer和实现java.util.TimerTask接口的类做定时任务功能)。
本文主要从大的框架方面介绍Quartz的基本使用和Quartz对用户提供的扩展点JobListener等监听接口。
通常对于作业调度我们关注这三个方面的内容:作业,调度时间,由谁调度。比如:我明天去北京。这就好中,将“去北京”看做一个作业,“我”就是这个作业的调度者,而“明天”自然就是调度的时间(也可以看作是去北京这个作业的触发时间)。
由上面的小情节可以看出框架能为我们做那些事呢?分离通用的,业务无关的部分组织成为解决某一问题或特定问题的模版代码即为框架。正好,Quartz就是作业调度方面或者领域的解决方案框架。再去看“去北京”这个关键词,可以发现去北京对于实际的业务而言仅仅是一个描述性的语句,而怎么去(灰机,BBC,还是绿皮车)好像这些并没能体现出来,而这样没有体现出来的好处就是框架和业务实现完全解耦。这里就要引入几个作业调度方面的专业名词,比如Job,Trigger ,Scheduler。我们这一这么组织这三个名词,比如:一个Schedule怀揣个Trigger,这个Trigger有个玩叫Job,时不时拿出来玩两下。至于玩一下Job,Job能够产生怎样的影响对于Trigger而言并不关心,不过这里似乎使得Job与Trigger产生了耦合,因为Job属于具体业务。对于一个完备的作业调度解决方案Quartz而言这样的问题已不是问题,Quartz用JobDetail来表示Job或者表达Job,从而让具有具体业务的Job与整个框架分离。
下面通过Quartz的类图来宏观的介绍Quartz2.x的使用。
1.Job和JobDetail
具体作业业务实现Job接口,具体在作业调度中使用JobDetail,使用JobBuilder构造器来构造具体的JobDetail。两个接口+一个构造者模式的JobDetail构造器完成了具体业务Job和实际作业调度中的JobDetail分离。实际应用中只要面向JobDetail和Job两个接口编程,隐藏了具体的实现如:JobDetailImpl类。
2.Triiger(作业触发器)
对于作业触发器大多与时间,日期,调度次数,间隔时间,调度延时等有关,Quartz对作业触发做了分类并提供了TriggerBuilder。
从上图可以看到Trigger接口下有不同的触发类型,如日常间隔触发(DailyTimeIntervalTriggerImpl),基于日历的触发,Cron出发,简单的触发。同样TriggerBuilder提供了构建Trigger的简便方式,使用者可以通过其来设置不同的属性信息构建具体的Trigger实现的实例。
3.作业调度器Scheduler
Quartz提供了不同类型的调度器实现,一般我们使用StdScheduler即可满足需要,如涉及到分布式或者远程方法调用则可以选择其它两种合适的实现。
4.Quartz提供的扩展之Listener
Quartz提供了各种Listener接口为用户观察作业,触发器,作业调度器执行过程提供了扩展点。
Scheduler对象具有ListenerManager的属性,ListenerManager对象用来管理TriggerListener,JobListener,SchedulerListener的实现,监听关系(比如:指定全局Job监听,指定特定的Job监听等),具体使用参见下文。
下面提供一组Quartz使用的示例代码:
package secondriver.springsubway.demo.job; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import org.quartz.impl.matchers.GroupMatcher; public class TestJobListener { public static Scheduler scheduler; @BeforeClass public static void setBeforeClass() throws SchedulerException { StdSchedulerFactory schedulerFactory = new StdSchedulerFactory(); scheduler = schedulerFactory.getScheduler(); scheduler.start(); } @AfterClass public static void setAfterClass() throws SchedulerException { if (scheduler.isStarted()) { scheduler.shutdown(true); } } //Prepared for Test /** * 使用Builder模式构建JobDetail实例 * @return */ public static JobDetail getJobDetail() { return JobBuilder.newJob(MyJob.class).withDescription("MyJobDetail") .withIdentity("myJob", "myJobGroup") .build(); } /** * 使用Builder模式构建Trigger实例 * @return */ public static Trigger getTrigger() { return TriggerBuilder.newTrigger().withDescription("MyTrigger").withIdentity("myTrigger", "myTriggerGroup") .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(3)) .startNow().build(); } @Test public void testOne() throws SchedulerException { JobDetail jobDetail = getJobDetail(); Trigger trigger = getTrigger(); scheduler.getListenerManager().addJobListener(new MyJobListener()); scheduler.getListenerManager().addTriggerListener(new MyTriggerListener()); scheduler.scheduleJob(jobDetail, trigger); } @Test public void testTwo() throws SchedulerException { JobDetail jobDetail = getJobDetail(); Trigger trigger = getTrigger(); /** * 为指定jobGrup添加JobListener */ scheduler.getListenerManager().addJobListener(new MyJobListener(), GroupMatcher.jobGroupEquals("myJobGroup")); /** * 为指定triggerGroup添加TriggerListener */ scheduler.getListenerManager().addTriggerListener(new MyTriggerListener(), GroupMatcher.triggerGroupEquals ("myTriggerGroup")); scheduler.scheduleJob(jobDetail, trigger); } @Test public void testThree() throws SchedulerException, InterruptedException { JobDetail jobDetail = getJobDetail(); //非持久化Job无关联Trigger添加到Scheduler需要使用addJob第三个参数storeNonDurableWhileAwaitingScheduling为true scheduler.addJob(jobDetail, false, true); //JobDetail 1<->* Trigger Trigger trigger1 = TriggerBuilder.newTrigger().forJob(jobDetail) .startNow().withSchedule(SimpleScheduleBuilder.repeatSecondlyForever()).build(); Trigger trigger2 = TriggerBuilder.newTrigger().forJob(jobDetail) .startNow().withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)).build(); scheduler.scheduleJob(trigger1); scheduler.scheduleJob(trigger2); Thread.sleep(10000); } /** * 具体业务实现类,实现Job接口的execute方法即可 */ public static class MyJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("MyJob " + this.getClass().getCanonicalName()); } } /** * JobListener实现 * <p/> * JobListener方法执行顺序 * <p/> * 如果TriigerListener的vetoJobExecution返回true * triggerFired -> vetoJobExecution ->jobExecutionVetoed * <p/> * 如果TiggerListener的vetoJobExecution返回false * triggerFired -> vetoJobExecution ->jobToBeExecuted -> [Job execute] -> jobWasExecuted * ->[triggerMisfired|triggerComplete] */ public static class MyJobListener implements JobListener { @Override public String getName() { return "MyJobListener"; } @Override public void jobToBeExecuted(JobExecutionContext context) { System.out.println("jobToBeExecuted:" + context.getJobDetail().getDescription()); } @Override public void jobExecutionVetoed(JobExecutionContext context) { System.out.println("jobExecutionVetoed:" + context.getJobDetail().getDescription()); } @Override public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { System.out.println("jobWasExecuted:" + context.getJobDetail().getDescription()); } } /** * TriggerListener实现 */ public static class MyTriggerListener implements TriggerListener { @Override public String getName() { return "MyTriggerListener"; } @Override public void triggerFired(Trigger trigger, JobExecutionContext context) { System.out.println("triggerFired:" + trigger.getDescription()); } @Override public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) { System.out.println("vetoJobExecution:" + trigger.getDescription()); return false; /** return:false triggerFired:MyTrigger vetoJobExecution:MyTrigger jobToBeExecuted:MyJobDetail MyJob secondriver.quartz.TestJobListener.MyJob jobWasExecuted:MyJobDetail triggerComplete:MyTrigger */ /** return:true triggerFired:MyTrigger vetoJobExecution:MyTrigger jobExecutionVetoed:MyJobDetail */ } @Override public void triggerMisfired(Trigger trigger) { System.out.println("triggerMisfired:" + trigger.getDescription()); } @Override public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) { System.out.println("triggerComplete:" + trigger.getDescription()); } } }
上述代码依赖Quartz框架的Maven配置:
<dependency> <groupId>org.quartz-scheduler</groupId> <version>2.2.1</version> <artifactId>quartz</artifactId> </dependency>
Quartz在Java应用作业调度方面提供了非常完备的实现,本文通过三个作业调度方面的关键词和Quartz的用户扩展Listener简要介绍了Quartz的使用,更多内容可以查看Quartz用户手册或者源代码。