尽可能的用声明式处理软件配置,其次才考虑编程式的方式。在上一篇《 》中,如果我们要在 Job 启动之后改变它的执行时间和频度,必须去修改源代码重新编译。这种方式只适用于小的例子程序,但是对于一个大且复杂的系统,这就成了一个问题了。因此,假如能以声明式部署 Quart Job 时,并且也是需求允许的情况下,你应该每次都选择这种方式 ·配置 quartz.properties 文件
文件 quartz.properties 定义了 Quartz 应用运行时行为,还包含了许多能控制 Quartz 运转的属性。这个文件应该放在classpath所指的路径下,比如我们这个java工程,就将它和下面将介绍的jobs.xml一起放在项目根目录下就是。如果不清楚就查看.classpath文件,它里面就配置了你的项目的classpath。
我们来看看最基础的 quartz.properties 文件,并讨论其中一些设置。下面是一个修剪版的 quartz.propertis文件
#============================================================================ # Configure Main Scheduler Properties #============================================================================ org.quartz.scheduler.instanceName = TestScheduler org.quartz.scheduler.instanceId = AUTO #============================================================================ # Configure ThreadPool #============================================================================ org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 3 org.quartz.threadPool.threadPriority = 5 #============================================================================ # Configure JobStore #============================================================================ org.quartz.jobStore.misfireThreshold = 60000 org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore #============================================================================ # Configure Plugins #============================================================================ org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingJobHistoryPlugin org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin org.quartz.plugin.jobInitializer.fileNames = jobs.xml org.quartz.plugin.jobInitializer.overWriteExistingJobs = true org.quartz.plugin.jobInitializer.failOnFileNotFound = true org.quartz.plugin.jobInitializer.scanInterval = 10 org.quartz.plugin.jobInitializer.wrapInUserTransaction = false ·调度器属性
第一部分有两行,分别设置调度器的实例名(instanceName) 和实例 ID (instanceId)。属性org.quartz.scheduler.instanceName 可以是你喜欢的任何字符串。它用来在用到多个调度器区分特定的调度器实例。多个调度器通常用在集群环境中。(Quartz 集群将会在第十一章,“Quartz 集群”中讨论)。现在的话,设置如下的一个字符串就行:org.quartz.scheduler.instanceName = QuartzScheduler实际上,这也是当你没有该属性配置时的默认值。
调度器的第二个属性是 org.quartz.scheduler.instanceId。和 instaneName 属性一样,instanceId 属性也允许任何字符串。这个值必须是在所有调度器实例中是唯一的,尤其是在一个集群当中。假如你想 Quartz 帮你生成这个值的话,可以设置为 AUTO。如果 Quartz 框架是运行在非集群环境中,那么自动产生的值将会是 NON_CLUSTERED。假如是在集群环境下使用 Quartz,这个值将会是主机名加上当前的日期和时间。大多情况下,设置为 AUTO 即可。·线程池属性
接下来的部分是设置有关线程必要的属性值,这些线程在 Quartz 中是运行在后台担当重任的。threadCount 属性控制了多少个工作者线程被创建用来处理 Job。原则上是,要处理的 Job 越多,那么需要的工作者线程也就越多。threadCount 的数值至少为 1。Quartz 没有限定你设置工作者线程的最大值,但是在多数机器上设置该值超过100的话就会显得相当不实用了,特别是在你的 Job 执行时间较长的情况下。这项没有默认值,所以你必须为这个属性设定一个值。
threadPriority 属性设置工作者线程的优先级。优先级别高的线程比级别低的线程更优先得到执行。threadPriority 属性的最大值是常量 java.lang.Thread.MAX_PRIORITY,等于10。最小值为常量 java.lang.Thread.MIN_PRIORITY,为1。这个属性的正常值是 Thread.NORM_PRIORITY,为5。大多情况下,把它设置为5,这也是没指定该属性的默认值。
最后一个要设置的线程池属性是 org.quartz.threadPool.class。这个值是一个实现了 org.quartz.spi.ThreadPool接口的类的全限名称。Quartz 自带的线程池实现类是 org.quartz.smpl.SimpleThreadPool,它能够满足大多数用户的需求。这个线程池实现具备简单的行为,并经很好的测试过。它在调度器的生命周期中提供固定大小的线程池。你能根据需求创建自己的线程池实现,如果你想要一个随需可伸缩的线程池时也许需要这么做。这个属性没有默认值,你必须为其指定值。
·作业存储设置
作业存储部分的设置描述了在调度器实例的生命周期中,Job 和 Trigger 信息是如何被存储的。我们还没有谈论到作业存储和它的目的;因为对当前例子是非必的,所以我们留待以后说明。现在的话,你所要了解的就是我们存储调度器信息在内存中而不是在关系型数据库中就行了。
把调度器信息存储在内存中非常的快也易于配置。当调度器进程一旦被终止,所有的 Job 和 Trigger 的状态就丢失了。要使 Job 存储在内存中需通过设置 org.quartz.jobStrore.class 属性为 org.quartz.simpl.RAMJobStore。假如我们不希望在 JVM 退出之后丢失调度器的状态信息的话,我们可以使用关系型数据库来存储这些信息。这需要另一个作业存储(JobStore) 实现,我们在后面将会讨论到。第五章“Cron Trigger 和其他”和第六章“作业存储和持久化”会提到你需要用到的不同类型的作业存储实现。·插件配置
在这个简单的 quartz.properties 文件中最后一部分是你要用到的 Quart 插件的配置。插件常常在别的开源框架上使用到,比如 Apache 的 Struts 框架(见 )。
一个声明式扩框架的方法就是通过新加实现了 org.quartz.spi.SchedulerPlugin 接口的类。SchedulerPlugin 接口中有给调度器调用的三个方法。
要在我们的例子中声明式配置调度器信息,我们会用到一个 Quartz 自带的叫做org.quartz.plugins.xml.JobInitializationPlugin 的插件。
默认时,这个插件会在 classpath 中搜索名为 quartz_jobs.xml 的文件并从中加载 Job 和 Trigger 信息。在下下面中讨论 quartz_jobs.xml 文件,这是我们所参考的非正式的 Job 定义文件。
·为插件修改 quartz.properties 配置
JobInitializationPlugin 找寻 quartz_jobs.xml 来获得声明的 Job 信息。假如你想改变这个文件名,你需要修改quartz.properties 来告诉插件去加载那个文件。例如,假如你想要 Quartz 从名为 my_quartz_jobs.xml 的 XML 文件中加载 Job 信息,你不得不为插件指定这一文件
org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingJobHistoryPlugin org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin org.quartz.plugin.jobInitializer.fileNames = jobs.xml org.quartz.plugin.jobInitializer.overWriteExistingJobs = true org.quartz.plugin.jobInitializer.failOnFileNotFound = true org.quartz.plugin.jobInitializer.scanInterval = 10 org.quartz.plugin.jobInitializer.wrapInUserTransaction = false 我们添加了属性 org.quartz.plugin.jobInitializer.fileName 并设置该属性值为我们想要的文件名。这个文件名要对classloader 可见,也就是说要在 classpath 下。
当 Quartz 启动后读取 quartz.properties 文件,然后初始化插件。它会传递上面配置的所有属性给插件,这时候插件也就得到通知去搜寻不同的文件。
下面就是目录扫描例子的 Job 定义的 XML 文件。正如上一篇所示例子那样,这里我们用的是声明式途径来配置 Job和 Trigger 信息的
<? xml version='1.0' encoding='utf-8' ?> < quartz xmlns ="http://www.opensymphony.com/quartz/JobSchedulingData" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.opensymphony.com/quartz/JobSchedulingData http://www.opensymphony.com/quartz/xml/job_scheduling_data_1_5.xsd" version ="1.5" > < job > < job-detail > < name > ScanDirectory </ name > < group > DEFAULT </ group > < description > A job that scans a directory for files </ description > < job-class > com.vista.quartz.ScanDirectoryJob </ job-class > < volatility > false </ volatility > < durability > false </ durability > < recover > false </ recover > < job-data-map allows-transient-data ="true" > < entry > < key > SCAN_DIR </ key > < value > D:\conf1 </ value > </ entry > </ job-data-map > </ job-detail > < trigger > < simple > < name > scanTrigger </ name > < group > DEFAULT </ group > < job-name > ScanDirectory </ job-name > < job-group > DEFAULT </ job-group > < start-time > 2008-09-03T14:43:00 </ start-time > <!-- repeat indefinitely every 10 seconds --> < repeat-count > -1 </ repeat-count > < repeat-interval > 10000 </ repeat-interval > </ simple > </ trigger > </ job > </ quartz > 在jobs.xml 中 <start-time> 的格式是:
< start-time > 2008-09-03T14:43:00 </ start-time > 其中T隔开日期和时间,默认时区 或者:
< start-time > 2008-09-03T14:43:00+08:00 </ start-time > 其中+08:00 表示东八区
<job> 元素描述了一个要注册到调度器上的 Job,相当于我们在前面章节中使用 scheduleJob() 方法那样。你所看到的<job-detail> 和 <trigger> 这两个元素就是我们在代码中以编程式传递给方法 schedulerJob() 的参数。前面本质上是与这里一样的,只是现在用的是一种较流行声明的方式。<trigger>元素也是非常直观的:它使用前面同样的属性,但更简单的建立一个 SimpleTrigger。因此仅仅是一种不同的(可论证的且更好的)方式做了上一篇代码 中同样的事情。显然,你也可以支持多个 Job。在上一篇代码 中我们编程的方式那么做的,也能用声明的方式来支持
<? xml version='1.0' encoding='utf-8' ?> < quartz xmlns ="http://www.opensymphony.com/quartz/JobSchedulingData" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://www.opensymphony.com/quartz/JobSchedulingData http://www.opensymphony.com/quartz/xml/job_scheduling_data_1_5.xsd" version ="1.5" > < job > < job-detail > < name > ScanDirectory1 </ name > < group > DEFAULT </ group > < description > A job that scans a directory for files </ description > < job-class > com.vista.quartz.ScanDirectoryJob </ job-class > < volatility > false </ volatility > < durability > false </ durability > < recover > false </ recover > < job-data-map allows-transient-data ="true" > < entry > < key > SCAN_DIR </ key > < value > D:\dyk\Java\Tomcat\conf </ value > </ entry > </ job-data-map > </ job-detail > < trigger > < simple > < name > scanTrigger1 </ name > < group > DEFAULT </ group > < job-name > ScanDirectory1 </ job-name > < job-group > DEFAULT </ job-group > < start-time > 2008-09-03T15:00:10 </ start-time > <!-- repeat indefinitely every 10 seconds --> < repeat-count > -1 </ repeat-count > < repeat-interval > 10000 </ repeat-interval > </ simple > </ trigger > </ job > < job > < job-detail > < name > ScanDirectory2 </ name > < group > DEFAULT </ group > < description > A job that scans a directory for files </ description > < job-class > com.vista.quartz.ScanDirectoryJob </ job-class > < volatility > false </ volatility > < durability > false </ durability > < recover > false </ recover > < job-data-map allows-transient-data ="true" > < entry > < key > SCAN_DIR </ key > < value > D:\dyk\Java\Tomcat\webapps\MyTest\WEB-INF </ value > </ entry > </ job-data-map > </ job-detail > < trigger > < simple > < name > scanTrigger2 </ name > < group > DEFAULT </ group > < job-name > ScanDirectory2 </ job-name > < job-group > DEFAULT </ job-group > < start-time > 2008-09-03T15:00:20 </ start-time > <!-- repeat indefinitely every 15 seconds --> < repeat-count > -1 </ repeat-count > < repeat-interval > 15000 </ repeat-interval > </ simple > </ trigger > </ job > </ quartz > 最后我们来看看原来的代码简化成如何了:
package com.vista.quartz; import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerUtils; import org.quartz.impl.StdSchedulerFactory; public class SimpleScheduler { static Log logger = LogFactory.getLog(SimpleScheduler. class ); public static void main(String[] args) { SimpleScheduler simple = new SimpleScheduler(); try { // Create a Scheduler and schedule the Job Scheduler scheduler = simple.createScheduler(); // Jobs can be scheduled after Scheduler is running scheduler.start(); logger.info( " Scheduler started at " + new Date()); } catch (SchedulerException ex) { logger.error(ex); } } public Scheduler createScheduler() throws SchedulerException { // 创建调度器 return StdSchedulerFactory.getDefaultScheduler(); } }