Quartz-Job 详解

在这里插入图片描述

概述

Quartz- Quartz API以及Jobs 和Triggers介绍 中 ,我们可以看到 Job是相当容易实现,只需要实现Job接口,重写execute方法即可.

Quartz 中可能需要为 Job 实例设置属性,这个功能通过 JobDetail 类来完成。

JobDetail 实例通过 JobBuilder 创建。你可以使用静态导入所有的方法,这样可以在你的代码中使用 DSL 风格:

import static org.quartz.JobBuilder.*;
 // 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.");
    }
  }

注意,我们给了调度器一个 JobDetail 实例,JobDetail 中提供了 Job 的 class 对象,因此它知道调用的 Job 类型。每次调度器执行 Job,它会在调用 execute(…) 方法前创建一个新的 Job 实例。当执行完成后,所有 Job 的引用将会丢弃,这些对象会被垃圾回收。

基于前面的描述,首先 Job 类需要一个无参构造方法,另外,在 Job 中存储状态属性是没有意义的,因为每次执行完成后,对象将会被删除。


JobDataMap

JobDataMap 可以用来保存数据对象(序列化)。JobDataMap 其实是 Java Map 接口的一个实现,并且添加了一些方便的方法用于存储和获取原始数据类型。

下面的例子将存储数据到 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 获取数据:

package com.xgj.quartz.quartzItself.jobDataMap;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;

public class MyJob2Runner {

	public static void main(String[] args) {

		try {
			// Grab the Scheduler instance from the Factory
			SchedulerFactory factory = new StdSchedulerFactory();
			Scheduler scheduler = factory.getScheduler();

			// start
			scheduler.start();

			// define the job and tie it to our MyJob class
			JobDetail job = newJob(MyJob2.class)
					.withIdentity("myJob", "group1")
					.usingJobData("jobSays", "Hello World!")
					.usingJobData("myFloatValue", 3.141f)
					.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
			scheduler.scheduleJob(job, trigger);

		} catch (SchedulerException e) {
			e.printStackTrace();
		}

	}
}

package com.xgj.quartz.quartzItself.jobDataMap;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;

public class MyJob2 implements Job {

	public MyJob2() {
	}

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

}

运行结果:

INFO  StdSchedulerFactory - Using default implementation for ThreadExecutor
INFO  SimpleThreadPool - Job execution threads will use class loader of thread: main
INFO  SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
INFO  QuartzScheduler - Quartz Scheduler v.2.2.3 created.
INFO  RAMJobStore - RAMJobStore initialized.
INFO  QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.2.3) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.

INFO  StdSchedulerFactory - Quartz scheduler 'DefaultQuartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
INFO  StdSchedulerFactory - Quartz scheduler version: 2.2.3
INFO  QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141
Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141
Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141
Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141
Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141
Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141
Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141
Instance group1.myJob of MyJob2 says: Hello World!, and val is: 3.141

...........
...........

如果你使用 JobStore 存储,那么你需要小心决定在 JobDataMap 中存放什么数据,因为对象将会序列化,因此会有一些 class 类型的问题。标准的 Java 类都非常安全,但是如果你要使用自己定义的类,那么任何时候你要改变类定义,都要小心不要破坏兼容性。你可以只保存 String 和原始数据类型从而消除可能发生的序列化问题。

如果你添加了 set 方法到你的 Job 类中,并且和 JobDataMap 中存放的键一致(例如,上面例子中添加 setJobSays(String val) 方法),然后 Quartz 默认的 JobFactory 实现将会自动在 Job 实例化的时候调用这些 set 方法。

Trigger 也可以关联 JobDataMap。这可用于当你需要在多个 Trigger 中使用相同的 Job 的时候,为每个 Job 设置不同的输入数据。JobDataMap 可以在 Job 执行期间从 JobExecutionContext 中获得。它将会合并 JobDetail 和 Trigger 中的 JobDataMap,如果名称相同,那么后者的值将会覆盖前者的值。

下面的例子将演示如何从 JobExecutionContext 中获取 JobDataMap:

public class MyJob implements Job {
 
    public MyJob() {
    }
 
    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");
   
      System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
    }
  }

或者,如果你想要依赖 JobFactory 注入映射值到你的类中,那么可以使用下面的代码:

package com.xgj.quartz.quartzItself.jobDataMap;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;

public class MyJob implements Job {

	private String jobSays;
	private float myFloatValue;

	public MyJob21() {
	}

	@Override
	public void execute(JobExecutionContext context)
			throws JobExecutionException {

		JobKey key = context.getJobDetail().getKey();

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

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

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

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

}

日志输出同上。


Job 实例

你可以创建一个 Job 类,然后通过创建多个 JobDetail 实例与 Job 关联,并保存到调度器中(每个任务都有自己的属性和 JobDataMap),这个 JobDetail 称为 Job 实例。

例如,你可以创建一个实现了 Job 接口的类,命名为“SalesReportJob”。这个类可以接收一个参数(通过 JobDataMap)用于定义销售报表基于哪个销售人员。它们可以创建多个 Job 实例(使用 JobDetail),例如 “SalesReportForJoe” 和 “SalesReportForMike”,这里使用了由 JobDataMap 传入 “joe” 和 “mike” 作为参数。

当 Trigger 被触发,关联的 JobDetail 将会被加载,并且 Job 类会通过 JobFactory 配置到 Scheduler。默认的 JobFactory 将会简单地调用 Job Class 的 newInstance() 方法,并尝试调用 set 方法将 JobDataMap 中同名的属性设置到 Job 中。


Job 状态和并发

有一组可添加到 Job 的 Annotation,可以影响 Quartz 的行为。

  • @DisallowConcurrentExecution 添加到 Job 类后,Quartz 将不会同时执行多个 Job 实例。我们用上一节的例子来讲解,如果 “SalesReportJob” 上添加了这个Annotation,那么同时只能执行一个“SalesReportForJoe”,但是却可以同时执行“SalesReportForMike”。因此,可以说这个约束是基于JobDetail 的而不是基于 Job 的。

  • @PersistJobDataAfterExecution 添加到 Job 类后,表示 Quartz 将会在成功执行 execute()方法后(没有抛出异常)更新 JobDetail 的JobDataMap,下一次执行相同的任务(JobDetail)将会得到更新后的值,而不是原始的值。就像@DisallowConcurrentExecution 一样,这个注释基于 JobDetail 而不是 Job 类的实例。

如果你使用了 @PersistJobDataAfterExecution 注释,那么强烈建议你使用 @DisallowConcurrentExecution 注释,这是为了避免出现并发问题,当多个 Job 实例同时执行的时候,到底使用了哪个数据将变得很混乱。


Job 的其它属性

下面列举了一些通过 JobDetail 定义的 Job 属性:

  • Durability – 持久性,如果 Job 是非持久性的,那么执行完 Job 后,如果没有任何活动的 Trigger与之关联,那么将会被调度器自动删除。换句话说,非持久性的 Job 的生命周期与它关联的 Trigger 相关。

  • RequestsRecovery – 如果任务设置了 RequestsRecovery,那么它在调度器发生硬停止(例如,当前进程crash,或者机器宕机)后,当调度器再次启动的时候将会重新执行。这种情况下,JobExecutionContext.isRecovering()方法将会返回 true。


JobExecutionException

最后,我们来看看 Job.execute(…) 方法。这个方法只允许抛出一种异常(包括 RuntimeException),那就是 JobExecutionException。正是因为如此,你通常需要将 execute() 方法中的所有内容放入 try-catch 语句块中。你也需要花点时间看看 JobExecutionException 的文档,你的任务可以使用它提供的各种指令来控制如何处理异常。

相关推荐
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页
实付 19.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值