Quartz学习(1)

介绍Quartz

 

Quartz是一个开源的任务调度系统,它能用来调度很多任务的执行。

 

运行环境

 

Quartz 能嵌入在其他应用程序里运行。

 

Quartz 能在一个应用服务器里被实例化(servlet容器), 并且参与XA

 

 

Quartz能独立运行(通过JVM,或者通过RMI

 

Quartz能被集群实例化

 

任务调度

 

当一个指定给任务的触发器发生时,任务就被调度执行触发器能被创建为:

 

一天的某个时间(精确到毫秒级)

 

一周的某些天

 

一个月的某些天

 

一年的某些天

 

不在一个Calendar列出的某些天 (例如工作节假日)

 

在一个指定的次数重复

 

重复到一个指定的时间/日期

 

无限重复

 

在一个间隔内重复

 

能够给任务指定名称和组名.触发器也能够指定名称和组名,这样可以很好的在调度器里组织

 

起来.一个加入到调度器里的任务可以被多个触发器注册。在J2EE环境里,任务能作为一个

 

分布式(XA)事务的一部分来执行。

 

任务执行

 

任务能够是任何实现Job接口的Java类。

 

任务类能够被Quartz实例化,或者被你的应用框架。

 

当一个触发器触发时,调度器会通知实例化了JobListener 

 

TriggerListener 接口的0个或者多个Java对象(监听器可以是简单的Java对象

 

EJBs, JMS发布者等). 在任务执行后,这些监听器也会被通知。

 

当任务完成时,他们会返回一个JobCompletionCode ,这个代码告诉调度

 

器任务执行成功或者失败.这个代码 也会指示调度器做一些动作-例如立即再

 

次执行任务。

 

任务持久化

 

Quartz的设计包含JobStore接口,这个接口能被实现来为任务的存储提供

 

不同的机制。

 

应用JDBCJobStore, 所有被配置成“稳定”的任务和触发器能通过JDBC

 

存储在关系数据库里。

 

应用RAMJobStore, 所有任务和触发器能被存储在RAM里因此不必在程

 

序重起之间保存-一个好处就是不必使用数据库。

 

事务

 

使用JobStoreCMTJDBCJobStore的子类),Quartz 能参与JTA事务。

 

Quartz 能管理JTA事务(开始和提交)在执行任务之间,这样,任务做的事

 

就可以发生在JTA事务里。

 

集群

 

Fail-over.

 

Load balancing.

 

监听器和插件

 

通过实现一个或多个监听接口,应用程序能捕捉调度事件来监控或控制任

 

/触发器的行为。

 

插件机制可以给Quartz增加功能,例如保持任务执行的历史记录,或从

 

一个定义好的文件里加载任务和触发器。

 

Quartz 装配了很多插件和监听器。

 

1.使用Quartz

 

在我们用调度器之前,调度器需要实例化。我们用SchedulerFactory 来实例它。一旦调度器

 

被实例,我们就可以启动它,置它为stand-by模式,最后关闭它。注意:一旦一个调度器被

 

关闭了,如果我们不重新实例化它,它就不可能被再次启动。直到调度器启动了或者当调度

 

器处于暂停状态,触发器才能够触发。下面有个简单的例子:

 

SchedulerFactory schedFact new org.quartz.impl.StdSchedulerFactory();

 

Scheduler sched schedFact.getScheduler();

 

sched.start();

 

JobDetail jobDetail new JobDetail("myJob",

 

null,

 

DumbJob.class);

 

Trigger trigger TriggerUtils.makeHourlyTrigger(); // 每个小时触发

 

trigger.setStartTime(TriggerUtils.getEvenHourDate(new Date())); // 在下个小时开始

 

trigger.setName("myTrigger");

 

sched.scheduleJob(jobDetail, trigger);

 

就象你看到的,使用Quartz是很简单的。在下一节我们介绍JobsTriggers

 

2.Jobs  Triggers

 

就象以前提到的,一个实现了Job接口的Java类就能够被调度器执行。接口如下:

 

package org.quartz;

 

public interface Job {

 

public void execute(JobExecutionContext context) throws JobExecutionException;

 

}

 

很简的,当Jobtrigger触发时,Jobexecute(..)方法就会被调度器调用。被传递到

 

这个方法里来的JobExecutionContext对象提供了带有job运行时的信息:执行它的调度器句

 

柄、触发它的触发器句柄、jobJobDetail对象和一些其他的项。

 

JobDetail对象是Job在被加到调度器里时所创建的,它包含有很多的Job属性设置,和

 

JobDataMap一样,可以用来存储job实例时的一些状态信息。

 

Trigger对象是用来触发执行Job的。当调度一个job时,我们实例一个触发器然后调整它

 

的属性来满足job执行的条件。触发器也有一个和它相关的JobDataMap,它是用来给被触

 

发器触发的job传参数的。Quartz有一些不同的触发器类型,不过,用得最多的是

 

SimpleTriggerCronTrigger

 

如果我们需要在给定时刻执行一次job或者在给定时刻触发job随后间断一定时间不停

 

的执行的话,SimpleTrigger是个简单的解决办法;如果我们想基于类似日历调度的触发job

 

的话,比如说,在每个星期五的中午或者在每个月第10天的1015触发job时,CronTrigger

 

是很有用的。

 

为什么用jobstriggers呢?很多任务调度器并没有任务和触发器的概念,一些任务调度器

 

简单定义一个“job”为在一个执行时间伴随一些小任务标示,其他的更像Quartzjob

 

trigger对象的联合体。在开发Quartz时,开发者们决定,在调度时间表和在这上面运行的

 

工作应该分开。这是很有用的。

 

例如,job能够独立于触发器被创建和储存在任务调度器里,并且,很多的触发器能够

 

与同一个job关联起来。这个松耦合的另一个好处就是在与jobs关联的触发器终止后,我们

 

能够再次配置保留在调度器里的jobs,这样的话,我们能够再次调度这些jobs而不需要重

 

新定义他们。我们也可以在不重新定义一个关联到job的触发器的情况下,修改或替代它。

 

Jobstriggers被注册到Quartz的调度器里时,他们就有了唯一标示符。他们也可以被放

 

到“groups”里,Groups是用来组织分类jobstriggers的,以便今后的维护。在一个组里

 

jobtrigger的名字必须是唯一的,换句话说,一个jobtrigger的全名为他们的名字加

 

上组名。如果把组名置为”null”,系统会自动给它置为Scheduler.DEFAULT_GROUP

 

现在,我们大概有了一些jobstriggers的理解,随后2节我们将根多的了解它们。

 

3.更多关于Jobs JobDetails

 

Jobs很容易实现,这儿有更多我们需要理解的东西:jobs的本质,job接口的execute(..)方法,

 

关于JobDetails

 

当我们实现的一个class是真正的”job”时,Quartz需要知道各种job有的属性,这是通

 

JobDetail类做到的。在没用JobDetail之前,JobDetail的功能的实现是通过在每个job

 

实现类上加上所有的现在JobDetailget方法来实现的。这就在每个job类上强加了一些实

 

现一样功能的代码,就显得每个job类很笨重,于是,Quartz开发者们就创造了JobDetail

 

类。

 

现在,我们来讨论一下在Quartzjob的本质和job实例的生命周期。首先我们来看看

 

第一节的代码片段: 

 

JobDetail jobDetail new JobDetail("myJob",      // job 名称

 

                     sched.DEFAULT_GROUP, // job组名(可以写'null'来用default group)

 

                     DumbJob.class);         //要执行的java

 

Trigger trigger TriggerUtils.makeDailyTrigger(8, 30);

 

trigger.setStartTime(new Date());

 

trigger.setName("myTrigger");

 

sched.scheduleJob(jobDetail, trigger);

 

现在我们定义“DumbJob”类:

 

public class DumbJob implements Job {

 

    public DumbJob() {

 

    }

 

    public void execute(JobExecutionContext context)

 

      throws JobExecutionException

 

    {

 

      System.err.println("DumbJob is executing.");

 

    }

 

}

 

可以看到我们给调度器一个JobDetail实例,并且,它通过job的类代码引用这个job来执行。

 

每次调度器执行job时,它会在调用jobexecute(..)方法之前创建一个他的实例。这就带来

 

了两个事实:一、job必须有一个不带参数的构造器,二、在job类里定义数据成员并没有

 

意义,因为在每次job执行的时候他们的值会被覆盖掉。

 

你可能现在想要问“我怎样给一个job实例提供属性/配置?”和“在几次执行间我怎样能

 

跟踪job的状态?”这些问题的答案是一样的:用JobDataMap- JobDetail对象的一部分。

 

JobDataMap

 

JobDataMap能够支持任何序列化的对象,当job执行时,这些对象能够在job实例中可用。

 

JobDataMap实现了Java Map接口,它有一些附加的方法,这些方法用来储存和跟踪简单类

 

型的数据。

 

如下代码可以很快地给job增加JobDataMap

 

jobDetail.getJobDataMap().put("jobSays", "Hello World!");

 

jobDetail.getJobDataMap().put("myFloatValue", 3.141f);

 

jobDetail.getJobDataMap().put("myStateData", new ArrayList());

 

job执行时,我们可以在job里通过如下代码得到JobDataMap

 

public class DumbJob implements Job {

 

    public DumbJob() {

 

    }

 

    public void execute(JobExecutionContext context)

 

      throws JobExecutionException

 

    {

 

      String instName context.getJobDetail().getName();

 

      String instGroup context.getJobDetail().getGroup();

 

      JobDataMap dataMap context.getJobDetail().getJobDataMap();

 

      String jobSays dataMap.getString("jobSays");

 

      float myFloatValue dataMap.getFloat("myFloatValue");

 

      ArrayList state (ArrayList)dataMap.get("myStateData");

 

      state.add(new Date());

 

      System.err.println("Instance instName of DumbJob says: jobSays);

 

    }

 

  }

 

如果用一个持久JobStore(在指南JobStore章节讨论),我们就应该注意在JobDataMap里放

 

些什么,因为在它里面的对象将会被序列化,并且这些对象会因此产生一些class-versioning

 

问题。明显的,标准Java类型应该是很安全的,但是,任何时候某人改变了一个你已经序

 

列化的实例的类的定义时,我们就要注意不能够破坏兼容性了。在这个方面的进一步信息可

 

以在Java Developer Connection Tech Tip: Serialization In The Real World里找到。我们能把

 

JDBC-JobStoreJobDataMap放到一个模式里,在那里,只有简单类型和String型能被储

 

存在Map里,从而消去任何以后的序列化问题。

 

Stateful vs. Non-Stateful Jobs

 

触发器也有与它们关联的JobDataMaps。假设我们有一个储存在调度器里被多个触发器关联

 

job,然而,对于每个独立的触发器,我想提供给job不同的数据输入,在这个时候,

 

JobDataMaps就很有用了。

 

job执行期间,JobDataMaps能够在JobExecutionContext里获得。JobDataMap融合在Trigger

 

JobDetail类里,JobDataMap里面的值能够利用key来更新。

 

以下例子显示,在job执行期间从JobExecutionContext里的JobDataMap得到数据:

 

public class DumbJob implements Job {

 

public DumbJob() {

 

    }

 

    public void execute(JobExecutionContext context)

 

      throws JobExecutionException

 

    {

 

      String instName context.getJobDetail().getName();

 

      String instGroup context.getJobDetail().getGroup();

 

      JobDataMap dataMap context.getJobDataMap();  // 注意:不同于以前的例子

 

      String jobSays dataMap.getString("jobSays");

 

      float myFloatValue dataMap.getFloat("myFloatValue");

 

      ArrayList state (ArrayList)dataMap.get("myStateData");

 

      state.add(new Date());

 

      System.err.println("Instance instName of DumbJob says: jobSays);

 

    }

 

  }

 

StatefulJob

 

现在,关于job状态数据的一些附加要点:一个job实例能定义为"有状态的"或者"无状态的

 

"。无状态的jobs仅当它们在被加入到调度器里时才存储JobDataMap。这就意味着,在jobs

 

执行期间对JobDataMap里数据的任何改变都会丢失,下次执行时job将看不到这些数据。

 

你可能会猜到,一个有状态的job就是它的反面例子-它的JobDataMap是在每次执行完job

 

后再次储存的。一个缺点就是有状态的job不能够并发执行。换句话说,如果job是有状态

 

的,一个触发器尝试触发这个已经执行了的job时,这个触发器就会等待直到这次执行结束。

 

用实现StatefulJob 接口来标记一个job是有状态的。

 

Job 'Instances'

 

我们能够创建一个单独的job类,并且通过创建多个JobDetails实例在调度器里储存很多它

 

的“实例定义”,每个都有它自己的属性集和JobDataMap ,把它们都加入到调度器里。

 

当一个触发器触发时,与它关联的job就是通过配置在调度器上的JobFactory 来实例化

 

的。默认的JobFactory简单的调用在job类上的newInstance()方法,你可能想要创建自己的

 

JobFactory实现来完成一些自己想要的事情,如:拥有应用程序的IoC或者DI容器进程/

 

始化job实例。

 

job的其他属性

 

这儿有一个其他属性的总结,这些属性是通过JobDetail对象为一个job实例定义的。

 

持久性– 如果一个job是非持久的,一旦没有任何可用的触发器与它关联

 

时,他就会自动得从调度器里被删除。

 

不稳定性-如果一个job是不稳定的,他就不会在重起Quartz调度器之间

 

持久化。

 

请求恢复– 如果一个job“请求恢复”,在调度器“硬关闭”(如:该进

 

程崩溃,机器被关掉)时这个job还在执行,过后,当调度器再次启动时,

 

他就会再次执行。在这种情况下,JobExecutionContext.isRecovering() 方法将

 

会返回true.

 

Job监听器 一个job能够有0个或者多个与它关联的监听器。当job执行

 

时,监听器就会被通知。在监听器的更多讨论请看TriggerListeners 

 

JobListeners

 

JobExecutionException

 

最后,我们来看看Job.execute(..)方法的一些细节。你能够从execute方法里抛出的仅有的异

 

常类型就是JobExecutionException。因为这样,我们应该使用try-catch块包围整个execute

 

方法内容。我们还应该花一些时间看看JobExecutionException文档。当job执行发生异常时,

 

通过设置JobExecutionException,可以让此job再次进入调度器或者今后不再运行。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ma_xs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值