[译]Java定时任务调度-Quartz文档(三)进一步讲讲Job和Job Detail

正如上篇文章所说的,Job很容易实现,只需要接口中唯一的execute方法。除此之外,你还需要稍微了解下Job、execute、Job interface和JobDetail的一些东西。

当你写的Job类执行特定任务时,Quartz需要知道这个类应该具有的各种属性。这就是前面所提到的JobDetail类完成的。

JobDetail实例是由JobBuilder创建的,可以这样引入:

import static org.quartz.JobBuilder.*;

让我们花一点时间来看看Job的原理和Quartz中Job的生命周期。首先看一下在第一篇文章中提及的一块代码片段:

// define the job and tie it to our HelloJob class
  JobDetail job = newJob(HelloJob.class)
      .withIdentity("myJob", "group1") // name "myJob", group "group1"
      .build();

  // Trigger the job to run now, and then every 40 seconds
  Trigger trigger = newTrigger()
      .withIdentity("myTrigger", "group1")
      .startNow()
      .withSchedule(simpleSchedule()
          .withIntervalInSeconds(40)
          .repeatForever())           
      .build();

  // Tell quartz to schedule the job using our trigger
  sched.scheduleJob(job, trigger);

HelloJob的定义如下:

public class HelloJob implements Job {

    public HelloJob() {
    }

    public void execute(JobExecutionContext context)
      throws JobExecutionException
    {
      System.err.println("Hello!  HelloJob is executing.");
    }
  }

注意传给scheduler的是一个JobDetail的实例,仅仅通过在创建JobDetail是指定了Job class,它就知道知道所执行的任务类型。每当scheduler执行任务的时候,总是创建一个新的类实例,在执行其execute方法。执行完毕后,会释放对该实例的引用,稍后将会被垃圾回收。这种做法要求Job类必须有一个无参的构造方法,还有就是静态域是无效的,因为无法在多个执行实例中共享。

你可能想问,如何对Job实例配置属性呢?或者换句话说,如何跟踪多个实例的执行呢?答案是使用JobDataMap,它属于JobDetail对象的一部分。

JobDataMap

JobDataMap是用来在执行过程中存储必要的数据对象。JobDataMap实现了Java Map接口,
还定义了一些存取基本数据类型的方法。
下面的例子介绍了在创建JobDetail时向JobDataMap中添加数据:

// define the job and tie it to our DumbJob class
  JobDetail job = newJob(DumbJob.class)
      .withIdentity("myJob", "group1") // name "myJob", group "group1"
      .usingJobData("jobSays", "Hello World!")
      .usingJobData("myFloatValue", 3.141f)
      .build();

下面的例子介绍在执行过程中如何从JobDataMap中取数据:

public class DumbJob implements Job {

    public DumbJob() {
    }

    public void execute(JobExecutionContext context)
      throws JobExecutionException
    {
      JobKey key = context.getJobDetail().getKey();

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

      String jobSays = dataMap.getString("jobSays");
      float myFloatValue = dataMap.getFloat("myFloatValue");

      System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
    }
  }

如果你使用持久化JobStore,必须注意放入JobDataMap中的数据类型,因为这个对象会被序列化,可能会有版本问题。显而易见,java的基本类型是没问题的,但是如果是用户定义的类型发生了变化,而存在之前序列化的对象,就必须要考虑兼容性问题了。当然,也可以限定JDBC-JobStore和JobDataMap只能存储基本类型和String,这样就排除了序列化带来的潜在问题。

如果你在Job类中定义了与JobDataMap中key对应的setter方法,Quartz的就会在job实例化是调用这些setter方法,所以应避免定义类似的方法,以保证map中数据的准确性。

Triigers也有对应的JobDataMap。当定义一个任务,关联到多分数据、多个触发器,定期、重复执行时,JobDataMap会很有用。
JobDataMap定义在Job执行过程中的JobExecutionContext中,由JobDetail中的JobDataMap和Trigger中的JobDataMap合并而成,如果有重名的变量,前面的将会被后面的所覆盖。
下面是从合并后的JobDataMap中取值的一个例子:


public class DumbJob implements Job {

    public DumbJob() {
    }

    public void execute(JobExecutionContext context)
      throws JobExecutionException
    {
      JobKey key = context.getJobDetail().getKey();

      JobDataMap dataMap = context.getMergedJobDataMap();  // Note the difference from the previous example

      String jobSays = dataMap.getString("jobSays");
      float myFloatValue = dataMap.getFloat("myFloatValue");
      ArrayList state = (ArrayList)dataMap.get("myStateData");
      state.add(new Date());

      System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
    }
  }

如果你想使用JobFactory将data map中的变量“注入”到你的类中,可以这样做:


  public class DumbJob implements Job {


    String jobSays;
    float myFloatValue;
    ArrayList state;

    public DumbJob() {
    }

    public void execute(JobExecutionContext context)
      throws JobExecutionException
    {
      JobKey key = context.getJobDetail().getKey();

      JobDataMap dataMap = context.getMergedJobDataMap();  // Note the difference from the previous example

      state.add(new Date());

      System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
    }

    public void setJobSays(String jobSays) {
      this.jobSays = jobSays;
    }

    public void setMyFloatValue(float myFloatValue) {
      myFloatValue = myFloatValue;
    }

    public void setState(ArrayList state) {
      state = state;
    }

  }

你应该能注意到,下面的代码总的来说更长,但是execute中的代码却变短了。有人会说虽然总代码量长,但是setter方法都可以用IDE自动生成,所以“手工”的代码实际是更短的,而且不必手动的从JobDataMap中取数据。怎么做,你决定!

Job “Instances”

很多用户花很长时间纠结于是谁创建了job实例。这里我们将就任务的状态和并发等问题,弄个明白。
你可以定义一个job类,然后创建多个JobDetail实例,每个实例都有自己的属性和JobDataMap,然后把它们关联到一个scheduler。
例如,你可以创建一个实现了Job interface的类“SalesReportJob”,这个job需要根据传进来的销售人员的姓名来确定销售报告。接着需要定义多个JobDetail,例如“SalesReportForJoe” and “SalesReportForMike”,并将“joe” and “mike”存入个子的JobDataMap中。

当触发器启动是,与之关联的JobDetail将会被加载,对应的job class会通过scheduler中配置的JobFactory实例化。默认的JobFactory会调用job class的newInstance方法,然后根据JobDataMap中的key值调用对应的setter方法进行变量赋值。你可能想创建自己的JobFactory实现来完成注入自己的IoC(控制反转)或者DI(依赖注入)。
在“Quartz speak”中,我们将存储的JobDetail看做“job definition”或者“JobDetail 实例”。通常来说,提到“job”,我们指的是一个具体的实现,即JobDetail;提到实现job interface的类,通常指的是“job class”。

Job State and Concurrency

接下来再看看任务的状态数据和并发问题。以下是Job类中可以使用的注解,这些注解会影响Quartz的行为。

@DisallowConcurrentExecution 添加到Job class,Quartz就不会并发的执行多个任务实例。
注意这里的用词。在前面的例子中,如果“SalesReportJob”添加了这个注解,那么同时只会有一个“SalesReportForJoe”实例运行,但这并不影响并发的执行“SalesReportForJoe”任务实例。这个约束是在JobDetail维度的,不是在Job class维度。

@PersistJobDataAfterExecution 添加了这个注解的job class,在执行完JobDetail后,会将其JobDataMap备份,以便于下次执行是使用,而不是使用默认的初始化数据。和第一个一样,这个约束也是在JobDetail维度,而不是Job class维度。

如果你使用@PersistJobDataAfterExecution注解,你需要慎重考虑是否要使用@DisallowConcurrentExecution注解,以避免当一个JobDetail并发执行时数据是否会相互影响。

Other Attributes Of Jobs

下面是其他可以通过JobDetail对象定义给job实例的属性归类:

  • Durability - 如果job是非持久的,与其相关的所有trigger触发执行完成之后将会自动删除,换计划说,所有job都是以与其相关的trigger结束而结束的。
  • RequestsRecovery - 如果job是“请求恢复”的,如果任务执行过程中schduler强制关闭了,那么当scheduler重新启动时,它将再次执行。在这种情况下JobExecutionContext.isRecovering()将返回TRUE;

JobExecutionException

最后,我们需要讲下Job.execute(..)的细节。在这个方法内部唯一可以排除的异常是JobExecutionException。鉴于此,可能会抛出的异常都应该使用tru/catch捕获处理。你也因该进一步看下JobExecutionException相关文档,以便更好的处理。

原文链接:
http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/tutorial-lesson-03.html

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。 Quartz的优势: 1、Quartz是一个任务调度框架(库),它几乎可以集成到任何应用系统中。 2、Quartz是非常灵活的,它让您能够以最“自然”的方式来编写您的项目的代码,实现您所期望的行为 3、Quartz是非常轻量级的,只需要非常少的配置 —— 它实际上可以被跳出框架来使用,如果你的需求是一些相对基本的简单的需求的话。 4、Quartz具有容错机制,并且可以在重启服务的时候持久化(”记忆”)你的定时任务,你的任务也不会丢失。 5、可以通过Quartz,封装成自己的分布式任务调度,实现强大的功能,成为自己的产品。6、有很多的互联网公司也都在使用Quartz。比如美团 Spring是一个很优秀的框架,它无缝的集成了Quartz,简单方便的让企业级应用更好的使用Quartz进行任务的调度。   课程说明:在我们的日常开发中,各种大型系统的开发少不了任务调度,简单的单机任务调度已经满足不了我们的系统需求,复杂的任务会让程序猿头疼, 所以急需一套专门的框架帮助我们去管理定时任务,并且可以在多台机器去执行我们的任务,还要可以管理我们的分布式定时任务。本课程从Quartz框架讲起,由浅到深,从使用到结构分析,再到源码分析,深入解析Quartz、Spring+Quartz,并且会讲解相关原理, 让大家充分的理解这个框架和框架的设计思想。由于互联网的复杂性,为了满足我们特定的需求,需要对Spring+Quartz进行二次开发,整个二次开发过程都会进行讲解。Spring被用在了越来越多的项目中, Quartz也被公认为是比较好用的定时器设置工具,学完这个课程后,不仅仅可以熟练掌握分布式定时任务,还可以深入理解大型框架的设计思想。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值