【Quartz】深入Job、JobDetail、JobDataMap、Trigger

林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka

Quartz API核心接口有:

  • Scheduler – 与scheduler交互的主要API;
  • Job – 你通过scheduler执行任务,你的任务类需要实现的接口;
  • JobDetail – 定义Job的实例;
  • Trigger – 触发Job的执行;
  • JobBuilder – 定义和创建JobDetail实例的接口;
  • TriggerBuilder – 定义和创建Trigger实例的接口;

一、Job

在上一节中,Job中定义了实际的业务逻辑,而JobDetail包含Job相关的配置信息。在Quartz中,每次Scheduler执行Job时,在调用其execute()方法之前,它需要先根据JobDetail提供的Job类型创建一个Job class的实例,在任务执行完以后,Job class的实例会被丢弃,Jvm的垃圾回收器会将它们回收。因此编写Job的具体实现时,需要注意:

(1) 它必须具有一个无参数的构造函数;

(2) 它不应该有静态数据类型,因为每次Job执行完以后便被回收,因此在多次执行时静态数据没法被维护。

在JobDetail中有这么一个成员JobDataMap,JobDataMap是Java Map接口的具体实现,并添加了一些便利的方法用于存储与读取原生类型数据,里面包含了当Job实例运行时,你希望提供给它的所有数据对象。

可以借助JobDataMap为Job实例提供属性/配置,可以通过它来追踪Job的执行状态等等。对于第一种情况,可以在创建Job时,添加JobDataMap数据,在Job的execute()中获取数据,第二种,则可以在Listener中通过获取JobDataMap中存储的状态数据追踪Job的执行状态。

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

packageorg.quartz;

publicinterface Job {

public void execute(JobExecutionContext context) throwsJobExecutionException;

}

很简的,当Job的trigger触发时,Job的execute(..)方法就会被调度器调用。被传递到这个方法里来的JobExecutionContext对象提供了带有job运行时的信息:执行它的调度器句柄、触发它的触发器句柄、job的JobDetail对象和一些其他的项。JobDetail对象是Job在被加到调度器里时所创建的,它包含有很多的Job属性设置,和JobDataMap一样,可以用来存储job实例时的一些状态信息。比如如下任务类

package com.mucfc;

import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class NewJob implements Job{
	 static Log logger = LogFactory.getLog(NewJob.class);
	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		 System.err.println("Hello!  NewJob is executing."+new Date() );
	//取得job详情
         JobDetail jobDetail = context.getJobDetail();
         // 取得job名称
         String jobName = jobDetail.getClass().getName();
         logger.info("Name: " + jobDetail.getClass().getSimpleName());
         //取得job的类
         logger.info("Job Class: " + jobDetail.getJobClass());
         //取得job开始时间
         logger.info(jobName + " fired at " + context.getFireTime());
        logger.info("Next fire time " + context.getNextFireTime());
	}

}

当 Scheduler 调用一个 Job,一个 JobexecutionContext 传递给 execute() 方法。JobExecutionContext 对象让 Job 能访问 Quartz 运行时候环境和 Job 本身的明细数据。这就类似于在 Java Web 应用中的 servlet 访问 ServletContext 那样。通过 JobExecutionContext,Job 可访问到所处环境的所有信息,包括注册到 Scheduler 上与该 Job 相关联的 JobDetail 和 Triiger。

二、JobDetail

JobDetail实例是通过JobBuilder类创建的

可以通过导入该类下的所有静态方法

       import static org.quartz.JobBuilder.*;

然后是创建:

        创建一个JobDetail实例
        JobDetail jobDetail = newJob(NewJob.class).withIdentity("job1_1", "jGroup1").build();

如果不导入静态包:

那么就要用:

	创建一个JobDetail实例
	obDetail jobDetail = JobBuilder.newJob(NewJob.class).withIdentity("job1_1", "jGroup1").build();

对于部署在 Scheduler 上的每一个 Job 只创建了一个 JobDetail 实例。JobDetail 是作为 Job 实例进行定义的。注意到在代码 中不是把 Job 对象注册到 Scheduler;实际注册的是一个 JobDetail 实例。

	public void startSchedule() {
		try {
			// 1、创建一个JobDetail实例,指定Quartz
			JobDetail jobDetail = JobBuilder.newJob(NewJob.class)	// 任务执行类
			.withIdentity("job1_1", "jGroup1")// 任务名,任务组
			.build();
			//2、创建Trigger
			SimpleScheduleBuilder builder=SimpleScheduleBuilder.simpleSchedule()
			.withIntervalInSeconds(5)		//设置间隔执行时间
			.repeatSecondlyForTotalCount(5);//设置执行次数

			Trigger trigger=TriggerBuilder.newTrigger().withIdentity(
					"trigger1_1","tGroup1").startNow().withSchedule(builder).build();
			//3、创建Scheduler
			Scheduler scheduler=StdSchedulerFactory.getDefaultScheduler();
			//4、调度执行
			scheduler.scheduleJob(jobDetail, trigger);
			scheduler.start();
		} catch (SchedulerException e) {
			e.printStackTrace();
		}

		}

结果:

从上面的代码中可以看JobDetail 被加到 Scheduler 中了,而不是 job。Job 类是作为 JobDetail 的一部份,但是它直到 Scheduler 准备要执行它的时候才会被实例化的。

直到执行时才会创建 Job 实例

Job 的实例要到该执行它们的时候才会实例化出来。每次 Job 被执行,一个新的 Job 实例会被创建。其中暗含的意思就是你的 Job 不必担心线程安全性,因为同一时刻仅有一个线程去执行给定 Job 类的实例,甚至是并发执行同一 Job 也是如此。

可以看到,我们传给scheduler一个JobDetail实例,因为我们在创建JobDetail时,将要执行的job的类名传给了JobDetail,所以scheduler就知道了要执行何种类型的job;每次当scheduler执行job时,在调用其execute(…)方法之前会创建该类的一个新的实例;执行完毕,对该实例的引用就被丢弃了,实例会被垃圾回收;这种执行策略带来的一个后果是,job必须有一个无参的构造函数(当使用默认的JobFactory时);另一个后果是,在job类中,不应该定义有状态的数据属性,因为在job的多次执行中,这些属性的值不会保留。

那么如何给job实例增加属性或配置呢?如何在job的多次执行中,跟踪job的状态呢?答案就是:JobDataMap,JobDetail对象的一部分。

三、JobDataMap

JobDataMap中可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型的数据的方法。

将job加入到scheduler之前,在构建JobDetail时,可以将数据放入JobDataMap,如下示例:

package com.mucfc;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
public class NewJob2 implements Job{

	@Override
	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.out.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);

	}

}

在job的执行过程中,可以从JobDataMap中取出数据,如下示例:

package com.mucfc;

import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class Test {
	public void startSchedule() {
		try {

			// 1、创建一个JobDetail实例,指定Quartz
			JobDetail jobDetail = JobBuilder.newJob(NewJob.class)	// 任务执行类
			.withIdentity("job1_1", "jGroup1")// 任务名,任务组
			.build();	

			JobDetail jobDetail2 = JobBuilder.newJob(NewJob2.class)
				      .withIdentity("job1_2", "jGroup1")
				      .usingJobData("jobSays", "Hello World!")
				      .usingJobData("myFloatValue", 3.141f)
				      .build();

			//2、创建Trigger
			SimpleScheduleBuilder builder=SimpleScheduleBuilder.simpleSchedule()
			.withIntervalInSeconds(5)		//设置间隔执行时间
			.repeatSecondlyForTotalCount(5);//设置执行次数

			Trigger trigger=TriggerBuilder.newTrigger().withIdentity(
					"trigger1_1","tGroup1").startNow().withSchedule(builder).build();
			//3、创建Scheduler
			Scheduler scheduler=StdSchedulerFactory.getDefaultScheduler();
			//4、调度执行
			scheduler.scheduleJob(jobDetail2, trigger);
			scheduler.start();
		} catch (SchedulerException e) {
			e.printStackTrace();
		}

		}
	public static void main(String[] args) {

		Test test=new Test();
		test.startSchedule();
	}

}

结果:

四、 Trigger

        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

Scheduler在使用之前需要实例化。一般通过SchedulerFactory来创建一个实例。有些用户将factory的实例保存在JNDI中,但直接初始化,然后使用该实例也许更简单(见下面的示例)。

scheduler实例化后,可以启动(start)、暂停(stand-by)、停止(shutdown)。注意:scheduler被停止后,除非重新实例化,否则不能重新启动;只有当scheduler启动后,即使处于暂停状态也不行,trigger才会被触发(job才会被执行)。

五、SimpleTrigger的介绍

正如其名所示,SimpleTrigger对于设置和使用是最为简单的一种 QuartzTrigger。它是为那种需要在特定的日期/时间启动,且以一个可能的间隔时间重复执行 n 次的 Job 所设计的。
我们前面已经在一个简单的Quartz的例子里使用过了SimpleTrigger,我们通过

	SimpleScheduleBuilder builder=SimpleScheduleBuilder.simpleSchedule()
			//设置间隔执行时间
			.withIntervalInSeconds(5)
			//设置执行次数
			.withRepeatCount(4);
	Trigger trigger=TriggerBuilder.newTrigger().withIdentity(
					"trigger1_1","tGroup1").startNow().withSchedule(builder).build();

或者无限执行

		SimpleScheduleBuilder builder=SimpleScheduleBuilder.simpleSchedule()
			//设置间隔执行时间
			.withIntervalInSeconds(5)
			//设置执行次数
			.repeatForever();
                Trigger trigger=TriggerBuilder.newTrigger().withIdentity(
                    "trigger1_1","tGroup1").startNow().withSchedule(builder).build();

对于Quartz而言,它还不能满足我们的触发情况,所以它仅仅是用于一些简单的触发情况;

六、CronTrigger

CronTrigger 允许设定非常复杂的触发时间表。然而有时也许不得不使用两个或多个 SimpleTrigger来满足你的触发需求,这时候,你仅仅需要一个 CronTrigger 实例就够了。顾名思义,CronTrigger 是基于 Unix类似于 cron 的表达式。例如,你也许有一个 Job,要它在星期一和星期五的上午 8:00-9:00间每五分钟执行一次。假如你试图用 SimpleTrigger 来实现,你或许要为这个 Job 配置多个Trigger。然而,你可以使用如下的表达式来产生一个遵照这个时间表触发的 Trigger;
比如:

	// 创建Trigger
	CronScheduleBuilder builder2 = CronScheduleBuilder.cronSchedule("0 0/5 8 * * *");//8:00-8:55,每隔5分钟执行
	/**
	builder2 = CronScheduleBuilder.dailyAtHourAndMinute(12, 30);
	**/
	Trigger trigger=TriggerBuilder.newTrigger().withIdentity("trigger1_1","tGroup1").startNow().withSchedule(builder2).build();

因为 CronTrigger内建的如此强的灵活性,也与生俱来可用于创建几乎无所限制的表达式,且因为支持unix的cron表达式,则做为企业应用,我们的操作系统一般也都以unxi操作系统为主,所以掌握CronTrigger的使用费用有必要,我们将在后面对CronTrigger 进行详细的介绍。cron 表达式的格式见下一节。

七、Job与Trigger的关系

大家都知道,一个作业,比较重要的三个要素就是Schduler,jobDetail,Trigger;而Trigger对于job而言就好比一个驱动器;没有触发器来定时驱动作业,作业就无法运行;对于Job而言,一个job可以对应多个Trigger,但对于Trigger而言,一个Trigger只能对应一个job;所以一个Trigger 只能被指派给一个 Job;如果你需要一个更复杂的触发计划,你可以创建多个 Trigger 并指派它们给同一个 Job。Scheduler 是基于配置在 Job上的 Trigger 来决定正确的执行计划的,下面就是为同一个 JobDetail 使用多个Trigger;

林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka

时间: 2024-08-30 15:22:44

【Quartz】深入Job、JobDetail、JobDataMap、Trigger的相关文章

Quartz 有状态的JobDataMap

Quartz,每次执行job,job永远是全新的对象,但是,如果job实现org.quartz.StatefulJob接口,而不是job接口. 此时JobDetail的JobDataMap将会共享一个对象. 注意: 当实现有状态接口,StatefulJob时,只有JobDetail的JobDataMap是共用的,其他的,比如,Job本身,Trigger等,仍然每次执行 的时候是全新的对象.所以,只有JobDetail的JobDataMap是共用的,其他的trigger.getJobDataMap

Quartz任务调度快速入门

Quartz任务调度快速入门 概述 了解Quartz体系结构 Quartz对任务调度的领域问题进行了高度的抽象,提出了调度器.任务和触发器这3个核心的概念,并在org.quartz通过接口和类对重要的这些核心概念进行描述: ●Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息.Job运行时的信息保存在JobDataMap实例中: ●Jo

Quartz任务调度快速入门(转)

概述 了解Quartz体系结构 Quartz对任务调度的领域问题进行了高度的抽象,提出了调度器.任务和触发器这3个核心的概念,并在org.quartz通过接口和类对重要的这些核心概念进行描述: ●Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息.Job运行时的信息保存在 JobDataMap实例中: ●JobDetail:Quartz

【Quartz】Quartz存储与持久化-基于quartz.properties的配置

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 一.   Quartz存储与持久化 Quartz提供两种基本作业存储类型.第一种类型叫做RAMJobStore,第二种类型叫做JDBC作业存储.在默认情况下Quartz将任务调度的运行信息保存在内存中,这种方法提供了最佳的性能,因为内存中数据访问最快.不足之处是缺乏数据的持久性,当程序路途停止或系统崩溃时,所有运行的信息都会丢失. 比如我们希望安排一个执行100次的任务,如果执行到50次时系

Quartz任务调度快速入门(转)

转自http://www.blogjava.net/baoyaer/articles/155645.html 概述 了解Quartz体系结构 Quartz对任务调度的领域问题进行了高度的抽象,提出了调度器.任务和触发器这3个核心的概念,并在org.quartz通过接口和类对重要的这些核心概念进行描述: ●Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调

quartz源码分析之深刻理解job,sheduler,calendar,trigger及listener之间的关系

org.quartz包 包org.quartz是Quartz的主包,包含了客户端接口. 其中接口有: Calendar接口: 定义了一个关联Trigger可能(或者不可能)触发的时间空间.它没有定义触发的真实时间,而是用在在普通的Schedule需要限制Trigger触发的时候.大部分Calendar包含默认所有的时间,并且用户去排除部分时间. 因而,可以把Calendar看做是排除了block时间的时间(例如:一个每5分钟触发一次的调度在周末(sunday)不执行,使用SimpleTrigge

quartz 定时任务的总结(三) trigger SimpleTrigger CronTrigger

看下图这三个的 关系: 看清关系之后写代码分析: MyScheduler 类: package cn.org.test; import cn.org.job.MyJob; import cn.org.job.MyJob02; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import java.text.SimpleDateFormat; import java.util.Date; /** * Created

Quartz(自动任务)中的触发器Trigger

1.Quartz中的触发器TriggerJob 包含了要执行任务的逻辑,但是 Job 对何时该执行却一无所知.这个事情留给了 Trigger.Quartz Trigger 继承了抽象的 org.quartz.Trigger 类.当前,Quartz 有三个可用的 Trigger: Java代码 ·org.quartz.SimpleTrigger ·org.quartz.CronTrigger ·org.quartz.NthIncludeDayTrigger 2.SimpleTrigger的介绍正如

Quartz使用(3) - Quartz核心接口Trigger

Trigger最常用的有两种SimpleTrigger和CronTrigger,首先介绍Trigger的一些基础的信息,然后会详细描述这两种Trigger. 1. 通用Trigger属性 quartz中所有的触发器Trigger都有一些共有属性,如TriggerKey,startTime等,这些属性可以使用TriggerBuilder进行设置.常用的属性举例如下: (1) triggerKey:触发器的标识,由名称与分组唯一指定,便于调度器调用与查找. (2) jobKey: 当触发器被触发时,