介绍Quartz
Quartz是一个开源的任务调度系统,它能用来调度很多任务的执行。
运行环境
Quartz
Quartz
务
Quartz能独立运行(通过JVM),或者通过RMI
Quartz能被集群实例化
任务调度
当一个指定给任务的触发器发生时,任务就被调度执行.
一天的某个时间(精确到毫秒级)
一周的某些天
一个月的某些天
一年的某些天
不在一个Calendar列出的某些天
在一个指定的次数重复
重复到一个指定的时间/日期
无限重复
在一个间隔内重复
能够给任务指定名称和组名.触发器也能够指定名称和组名,这样可以很好的在调度器里组织
起来.一个加入到调度器里的任务可以被多个触发器注册。在J2EE环境里,任务能作为一个
分布式(XA)事务的一部分来执行。
任务执行
任务能够是任何实现Job接口的Java类。
任务类能够被Quartz实例化,或者被你的应用框架。
当一个触发器触发时,调度器会通知实例化了JobListener
TriggerListener
EJBs,
当任务完成时,他们会返回一个JobCompletionCode
器任务执行成功或者失败.这个代码
次执行任务。
任务持久化
Quartz的设计包含JobStore接口,这个接口能被实现来为任务的存储提供
不同的机制。
应用JDBCJobStore,
存储在关系数据库里。
应用RAMJobStore,
序重起之间保存-一个好处就是不必使用数据库。
事务
使用JobStoreCMT(JDBCJobStore的子类),Quartz
Quartz
就可以发生在JTA事务里。
集群
Fail-over.
Load
监听器和插件
通过实现一个或多个监听接口,应用程序能捕捉调度事件来监控或控制任
务/触发器的行为。
插件机制可以给Quartz增加功能,例如保持任务执行的历史记录,或从
一个定义好的文件里加载任务和触发器。
Quartz
1.使用Quartz
在我们用调度器之前,调度器需要实例化。我们用SchedulerFactory
被实例,我们就可以启动它,置它为stand-by模式,最后关闭它。注意:一旦一个调度器被
关闭了,如果我们不重新实例化它,它就不可能被再次启动。直到调度器启动了或者当调度
器处于暂停状态,触发器才能够触发。下面有个简单的例子:
SchedulerFactory
Scheduler
sched.start();
JobDetail
null,
DumbJob.class);
Trigger
trigger.setStartTime(TriggerUtils.getEvenHourDate(new
trigger.setName("myTrigger");
sched.scheduleJob(jobDetail,
就象你看到的,使用Quartz是很简单的。在下一节我们介绍Jobs和Triggers。
2.Jobs
就象以前提到的,一个实现了Job接口的Java类就能够被调度器执行。接口如下:
package
public
public
}
很简的,当Job的trigger触发时,Job的execute(..)方法就会被调度器调用。被传递到
这个方法里来的JobExecutionContext对象提供了带有job运行时的信息:执行它的调度器句
柄、触发它的触发器句柄、job的JobDetail对象和一些其他的项。
JobDetail对象是Job在被加到调度器里时所创建的,它包含有很多的Job属性设置,和
JobDataMap一样,可以用来存储job实例时的一些状态信息。
Trigger对象是用来触发执行Job的。当调度一个job时,我们实例一个触发器然后调整它
的属性来满足job执行的条件。触发器也有一个和它相关的JobDataMap,它是用来给被触
发器触发的job传参数的。Quartz有一些不同的触发器类型,不过,用得最多的是
SimpleTrigger和CronTrigger。
如果我们需要在给定时刻执行一次job或者在给定时刻触发job随后间断一定时间不停
的执行的话,SimpleTrigger是个简单的解决办法;如果我们想基于类似日历调度的触发job
的话,比如说,在每个星期五的中午或者在每个月第10天的10:15触发job时,CronTrigger
是很有用的。
为什么用jobs和triggers呢?很多任务调度器并没有任务和触发器的概念,一些任务调度器
简单定义一个“job”为在一个执行时间伴随一些小任务标示,其他的更像Quartz里job和
trigger对象的联合体。在开发Quartz时,开发者们决定,在调度时间表和在这上面运行的
工作应该分开。这是很有用的。
例如,job能够独立于触发器被创建和储存在任务调度器里,并且,很多的触发器能够
与同一个job关联起来。这个松耦合的另一个好处就是在与jobs关联的触发器终止后,我们
能够再次配置保留在调度器里的jobs,这样的话,我们能够再次调度这些jobs而不需要重
新定义他们。我们也可以在不重新定义一个关联到job的触发器的情况下,修改或替代它。
当Jobs和triggers被注册到Quartz的调度器里时,他们就有了唯一标示符。他们也可以被放
到“groups”里,Groups是用来组织分类jobs和triggers的,以便今后的维护。在一个组里
的job和trigger的名字必须是唯一的,换句话说,一个job和trigger的全名为他们的名字加
上组名。如果把组名置为”null”,系统会自动给它置为Scheduler.DEFAULT_GROUP
现在,我们大概有了一些jobs和triggers的理解,随后2节我们将根多的了解它们。
3.更多关于Jobs
Jobs很容易实现,这儿有更多我们需要理解的东西:jobs的本质,job接口的execute(..)方法,
关于JobDetails。
当我们实现的一个class是真正的”job”时,Quartz需要知道各种job有的属性,这是通
过JobDetail类做到的。在没用JobDetail之前,JobDetail的功能的实现是通过在每个job的
实现类上加上所有的现在JobDetail的get方法来实现的。这就在每个job类上强加了一些实
现一样功能的代码,就显得每个job类很笨重,于是,Quartz开发者们就创造了JobDetail
类。
现在,我们来讨论一下在Quartz里job的本质和job实例的生命周期。首先我们来看看
第一节的代码片段:
JobDetail
Trigger
trigger.setStartTime(new
trigger.setName("myTrigger");
sched.scheduleJob(jobDetail,
现在我们定义“DumbJob”类:
public
}
可以看到我们给调度器一个JobDetail实例,并且,它通过job的类代码引用这个job来执行。
每次调度器执行job时,它会在调用job的execute(..)方法之前创建一个他的实例。这就带来
了两个事实:一、job必须有一个不带参数的构造器,二、在job类里定义数据成员并没有
意义,因为在每次job执行的时候他们的值会被覆盖掉。
你可能现在想要问“我怎样给一个job实例提供属性/配置?”和“在几次执行间我怎样能
跟踪job的状态?”这些问题的答案是一样的:用JobDataMap-
JobDataMap
JobDataMap能够支持任何序列化的对象,当job执行时,这些对象能够在job实例中可用。
JobDataMap实现了Java
型的数据。
如下代码可以很快地给job增加JobDataMap:
jobDetail.getJobDataMap().put("jobSays",
jobDetail.getJobDataMap().put("myFloatValue",
jobDetail.getJobDataMap().put("myStateData",
在job执行时,我们可以在job里通过如下代码得到JobDataMap:
public
如果用一个持久JobStore(在指南JobStore章节讨论),我们就应该注意在JobDataMap里放
些什么,因为在它里面的对象将会被序列化,并且这些对象会因此产生一些class-versioning
问题。明显的,标准Java类型应该是很安全的,但是,任何时候某人改变了一个你已经序
列化的实例的类的定义时,我们就要注意不能够破坏兼容性了。在这个方面的进一步信息可
以在Java
JDBC-JobStore和JobDataMap放到一个模式里,在那里,只有简单类型和String型能被储
存在Map里,从而消去任何以后的序列化问题。
Stateful
触发器也有与它们关联的JobDataMaps。假设我们有一个储存在调度器里被多个触发器关联
的job,然而,对于每个独立的触发器,我想提供给job不同的数据输入,在这个时候,
JobDataMaps就很有用了。
在job执行期间,JobDataMaps能够在JobExecutionContext里获得。JobDataMap融合在Trigger
和JobDetail类里,JobDataMap里面的值能够利用key来更新。
以下例子显示,在job执行期间从JobExecutionContext里的JobDataMap得到数据:
public
public
StatefulJob
现在,关于job状态数据的一些附加要点:一个job实例能定义为"有状态的"或者"无状态的
"。无状态的jobs仅当它们在被加入到调度器里时才存储JobDataMap。这就意味着,在jobs
执行期间对JobDataMap里数据的任何改变都会丢失,下次执行时job将看不到这些数据。
你可能会猜到,一个有状态的job就是它的反面例子-它的JobDataMap是在每次执行完job
后再次储存的。一个缺点就是有状态的job不能够并发执行。换句话说,如果job是有状态
的,一个触发器尝试触发这个已经执行了的job时,这个触发器就会等待直到这次执行结束。
用实现StatefulJob
Job
我们能够创建一个单独的job类,并且通过创建多个JobDetails实例在调度器里储存很多它
的“实例定义”,每个都有它自己的属性集和JobDataMap
当一个触发器触发时,与它关联的job就是通过配置在调度器上的JobFactory
的。默认的JobFactory简单的调用在job类上的newInstance()方法,你可能想要创建自己的
JobFactory实现来完成一些自己想要的事情,如:拥有应用程序的IoC或者DI容器进程/初
始化job实例。
job的其他属性
这儿有一个其他属性的总结,这些属性是通过JobDetail对象为一个job实例定义的。
持久性–
时,他就会自动得从调度器里被删除。
不稳定性-如果一个job是不稳定的,他就不会在重起Quartz调度器之间
持久化。
请求恢复–
程崩溃,机器被关掉)时这个job还在执行,过后,当调度器再次启动时,
他就会再次执行。在这种情况下,JobExecutionContext.isRecovering()
会返回true.
Job监听器
时,监听器就会被通知。在监听器的更多讨论请看TriggerListeners
JobListeners
JobExecutionException
最后,我们来看看Job.execute(..)方法的一些细节。你能够从execute方法里抛出的仅有的异
常类型就是JobExecutionException。因为这样,我们应该使用try-catch块包围整个execute
方法内容。我们还应该花一些时间看看JobExecutionException文档。当job执行发生异常时,
通过设置JobExecutionException,可以让此job再次进入调度器或者今后不再运行。