`
suichangkele
  • 浏览: 198171 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

quartz学习02-job以及jobDetail(1)

阅读更多

介绍一下job的生命周期,每当trigger触发器出发的时候都会创建一个新的job实例,也就是在上一个博客中的HelloJob的实例每执行一次调度就会实例化一次,等实例化完成之后就会被垃圾回收。我们可以这样做一下实验,以验证这个:

public class HelloJob implements Job {
	
	static int i = 0;
	public HelloJob(){
		i++;
	}

	public void execute(JobExecutionContext context) throws JobExecutionException {
		System.out.println("现在的i是: " + i);	
	}
}

然后我们执行定时任务后发现i是会不停的增长的,也就是说每一次执行都是调用了构造方法,创建了新的实例。

 

我们在上一博客中写了将JobDetail的组成是由job的,也就是执行的代码,但是我们传入的是一个.class文件,

JobDetail job = newJob(HelloJob.class).withIdentity("myJob", Scheduler.DEFAULT_GROUP).xxxx

 很容易就会想到,一定是用的反射,也就是newInstance的方法,这个方法要求有一个无参数的构造方法。现在就存在这样的一个问题,如果我要求执行的job的execute方法中含有依赖于其他内容的代码呢,也就是说execute中执行的方法不是定死的,是在运行中产生的,因为采用的是反射,我们无法向job的实现类的的实例通过setter方法或者是构造方法传入值,所以这是一个问题。

 

那么如何解决呢,文档中给了我们答案:使用JobDataMap进行传值。

文档中给出的在创建JobDetail时使用JobDataMap进行传值的代码:

JobDetail job = newJob(DumbJob.class)
      .withIdentity("myJob", "group1") // name "myJob", group "group1"
      .usingJobData("jobSays", "Hello World!")
      .usingJobData("myFloatValue", 3.141f)
      .build();

 我通过实验发现,也可以在创建完jobDetail之后设置,代码如下:

//创建job任务
JobDetail job = newJob(HelloJob.class).withIdentity("myJob", Scheduler.DEFAULT_GROUP).build();
job.getJobDataMap().put("name1", "jobDetail");
job.getJobDataMap().put("name2", "job2");  //使用的方法和map一样。

 在Job的实现类中,我们这里是HelloJob,可以通过传入的参数JobExecutionContext获得JobDetail和Trigger,来自官网的代码:

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);
}

如果我们在Job的实现类中设置了setter方法,那么在执行execute方法之前会先执行那些setter方法,但是执行的方法是和从JobDataMap相关的,之后在JobDataMap中存在的key的属性的setter方法才能执行,我们看官网的例子,他设置了jobSays、myFloatValue这两个属性,那么会执行setJobSays和setMyFloatValue这两个方法(如果这两个方法存在的话),但是不会执行其他setter方法。也就是说通过JobDataMap传入的值可以通过在Job中设置setter属性来传入到通过反射设置的job实例中,然后我们就可以通过在execute方法中调用调用getter方法来获取这些值了,这样就免去了必须从传入的JobExecutionContext中获取值了。

 

不光是是有JobDetail含有JobDataMap,在Trigger中也可以设置,设置的方法和JobDetail一样,我们也可以在JoBExecutionContext中获得设置的值。文档中还提到我们可以通过JobExecutionContext获得一个JobDataMap,这里面的值是合并了JobDetail中的map和Trigger中的map,后者会覆盖前者的值,如果key相同的话。通过这个Map我们就可以获得所有要传入的值了,获得方法:JobDataMap map = context.getMergedJobDataMap()

 

现在有个疑问,传入到execute方法中的参数是复制了一份参数呢,还是就是把原来放入到JobDataMap中参数不复制的传入呢?官网上的文档说的是复制一份,我们做一个实验:

如果不是复制的话,如果我们在execute方法中改变了JobDetail中的map,那么我们在主动调度中也能获得改变后的值,测试下能不能获得。代码如下:

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.impl.StdScheduler;
import org.quartz.impl.StdSchedulerFactory;
import static    org.quartz.TriggerBuilder.newTrigger;
import job.CheckJobDataMapCopyOrNotJob;
/**
 * 检查传入到job中的参数是不是复制
 * @author wuguohua
 *
 */
public class CheckJobDataMapCopyOrNot {
	
	public static void main(String[] args) throws SchedulerException, InterruptedException {
	    StdSchedulerFactory fac = new StdSchedulerFactory();
	    StdScheduler scheduler = (StdScheduler) fac.getScheduler();
	    scheduler.start();
	    
	    JobDetail jobDetail = JobBuilder.newJob(CheckJobDataMapCopyOrNotJob.class)
	    		.withIdentity("CheckJobDataMapCopyOrNotJobDetail", Scheduler.DEFAULT_GROUP).build();
	    
	    jobDetail.getJobDataMap().put("name", "1");
	    
    Trigger trigger = newTrigger().withIdentity("CheckJobDataMapCopyOrNotTrigger", Scheduler.DEFAULT_GROUP).startNow()
	    		.withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(100,3)).build();//这个表示每3秒执行一次,一共执行100次。
	    trigger.getJobDataMap().put("age", 1);
	    scheduler.scheduleJob(jobDetail, trigger);
	    
	    for(;true;){
	    	System.out.println("xxxxx        " + jobDetail.getJobDataMap().get("name") + "      " + trigger.getJobDataMap().get("age"));
	    	Thread.sleep(1000*2);
	    }
	      		
	}
}

 Job的代码:

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class CheckJobDataMapCopyOrNotJob implements Job {
	public void execute(JobExecutionContext context) throws JobExecutionException {
		JobDataMap map1 = context.getJobDetail().getJobDataMap();
		JobDataMap map2 = context.getTrigger().getJobDataMap();
  		System.out.println("yyyy         "  + map1.get("name") + "    " +  map2.get("age"));
		 map1.put("name", "2");
		 map2.put("age",2);
	}
}

我们在job中改变了name和age的值,如果不是复制的话,在xxxx开头的打印中也会出现2才对。

结果如下:

 

xxxxx        1      1
yyyy         1    1
xxxxx        1      1
yyyy         1    1
xxxxx        1      1
yyyy         1    1
xxxxx        1      1
xxxxx        1      1
yyyy         1    1
xxxxx        1      1
.......

 结果显示无论是xxxx还是yyyy都是打印的1,证明真的是将参数也复制了一份。

我证明这个原因是文档中提到了可以通过某个方法来实现在执行execute之后将改变的值回传给JobDetail中的JobDataMap,方法是在Job的实现类上加上这个注解:

@PersistJobDataAfterExecution,我在CcheckJobDataCopyOrNotJob加上之后,做的测试结果如下:

xxxxx        1      1
yyyy         1    1
xxxxx        1      1
yyyy         2    1
xxxxx        1      1
yyyy         2    1
xxxxx        1      1
xxxxx        1      1
yyyy         2    1
xxxxx        1      1
yyyy         2    1
xxxxx        1      1
xxxxx        1      1

 结果证明了JobDetail中的map的确是被更新了,但是Trigger中的map并没有更新,文档中并没有提及Trigger也会被更新。

文档中还提及了一个概念,如果一个job的执行时间大于调度周期怎么办?也就是说如果完成一次调度需要2秒,而调度周期只有1秒,怎么办。quartz提供了可以配置的方案,也就是可以并发,也可以防止并发。默认情况下是可以并发的,可以通过在Job的实现类上加 @DisallowConcurrentExecution来实现。

我得测试代码如下:  调度类:

import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.impl.StdScheduler;
import org.quartz.impl.StdSchedulerFactory;

import job.HelloJob;

public class Demo1 {
	public static void main(String[] args) throws InterruptedException {
		try {
			 StdSchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
 			StdScheduler sched = (StdScheduler) schedFact.getScheduler();
 			 //开始调度器
			 sched.start();
			//创建job任务
			JobDetail job = newJob(HelloJob.class).withIdentity("myJob", Scheduler.DEFAULT_GROUP).build();
			//创建trigger,触发器
			 Trigger trigger = newTrigger().withIdentity("myTrigger",Scheduler.DEFAULT_GROUP).startNow()
					           .withSchedule(SimpleScheduleBuilder.repeatSecondlyForTotalCount(100,3)).build();//3秒调度一次
 			//将任务和触发器绑定到调度器
			 sched.scheduleJob(job, trigger);
 			Thread.sleep(1000000);
 			sched.shutdown();
		 } catch (SchedulerException se) {
			 se.printStackTrace();
		}
	}
}

 工作类的execute方法:

public void execute(JobExecutionContext context) throws JobExecutionException {
	System.out.println("helloJob     begin");
	try {
		Thread.sleep(5*1000);            //睡眠五秒
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	System.out.println("helloJob    end");
}

如果可以并发执行的话,那么一开始会出现两个"helloJob    begin",因为5秒之后才会输出"helloJob     end"。我们不加@DisallowConcurrentExecution时的结果如下:

 

helloJob     begin
helloJob     begin
helloJob    end
helloJob     begin
helloJob    end
helloJob     begin
helloJob    end
helloJob     begin
..... 

加了之后的结果是:

helloJob     begin
helloJob    end
helloJob     begin
helloJob    end
helloJob     begin
helloJob    end
helloJob     begin

 并且执行的速度要慢得多。这里也验证了官网的文档。

 

官网还推荐,这两个一定要配合使用,当使用@persistJobDataAfterExecution时,一定要使用@DisallowConcurrentExecution,因为要修改JobDetail中的map,如果不加同步限制的话会造成对JobDetail的map的并发修改。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    quartz-all-1.6.0.jar包定时任务jar

    3. **Scheduler**: Scheduler是整个Quartz框架的中心,它负责管理所有的Job和Trigger,以及它们之间的关联。通过调用Scheduler的start()方法启动调度服务,然后使用`scheduleJob(JobDetail, Trigger)`方法将Job和...

    quartz-2.3.0-distribution.zip

    "quartz-2.3.0-distribution.zip"这个压缩包包含了Quartz库的2.3.0版本及相关资源,对于开发者来说是学习和使用Quartz的重要资料。 1. **Quartz的核心概念** - **Job**:Quartz中的工作单元,代表一个需要执行的...

    quartz-all-1.6.0.jar

    1. **灵活的调度**:Quartz 提供了丰富的调度API,可以设置定时任务执行一次、每天重复、每周重复等,甚至可以定义复杂的触发规则,如在某些特定时间点触发。 2. **可持久化**:Quartz 支持将任务和触发器存储在...

    quartz和spring-quartz

    通过JobDetail来配置Job的属性,如Job的名称、组别、数据绑定等。 2. **Trigger**: 触发器决定了Job的执行时间。Quartz提供了多种Trigger类型,如SimpleTrigger(简单触发器)用于一次性或间隔执行,CronTrigger...

    quartz-2.2.2-distribution.rar

    综上所述,这个压缩包提供的 Quartz 2.2.2 版本和 Spring Boot 2.x 的集成示例,对于学习和使用 Quartz 进行任务调度具有很高的参考价值。无论是初学者还是经验丰富的开发者,都可以从中受益,快速掌握如何在 Spring...

    Quartz-Job-Scheduling-Framework-中文版-V0.9.1.zip

    还介绍了 Job、JobExecutionContext、JobDetail、JobDataMap,及如何访问 JobDataMap 中的数据。有状态和无状态的 Job。 第四章. 部署 Job (第三部分) 内容提要:Job 的易失性、持久性和可恢复性,如何从 Scheduler...

    quartzall1.6.0jar_quartz-all-1.6.0.jar_

    1. **任务与触发器**:在Quartz中,任务被称为Job,触发器则是控制何时执行这些任务的规则。Job代表了需要执行的工作,而Trigger定义了Job何时应该被执行。你可以创建一次性或周期性的触发器,如每分钟、每天或每周...

    quartz-2.3.0-distribution.tar.gz

    4. **JobDetail(作业详情)**:封装了 Job 类的信息,包括 Job 类的类名、关联的数据以及 Job 是否持久化等设置。 5. **Calendar(日历)**:Quartz 支持结合 Trigger 使用自定义的日历,以便在特定日期或时间段内...

    quartz-2.2.3-distribution.zip

    下面我们将深入探讨Quartz的核心特性、功能、使用场景以及如何进行配置。 1. **Quartz核心特性**: - **可移植性**:Quartz设计为完全跨平台,可以在任何支持Java的环境中运行。 - **灵活的调度**:它支持简单与...

    spring-context-support.jar,quartz-all-1.6.0.jar

    `quartz-all-1.6.0.jar`包含了Quartz的所有功能模块,包括核心API、JDBC作业存储支持以及一些其他的实用工具。 集成Quartz到Spring项目中,首先需要在Spring的配置文件中定义一个`SchedulerFactoryBean`,这将负责...

    Multiple-Jobs-in-Quartz-Spring-Example

    JobDetail 包含了 Job 的类信息以及相关的数据绑定。我们可以使用 Spring 的 `JobDetailFactoryBean` 来简化这一过程。 ```java @Bean public JobDetail myFirstJobDetail() { return JobBuilder.newJob...

    quartz-2.2 需要的jar以及src源代码

    1. **Quartz简介** - Quartz是一个轻量级的作业调度框架,完全用Java编写,可以嵌入到任何Java应用中。 - 它基于JDBC存储作业和触发器,支持集群和分布式环境。 2. **版本2.2.3** - Quartz 2.2.3是该框架的一个...

    quartz-2.2.3-官方文档数据及PDM表结构.zip

    2. **调度器(Scheduler)**:调度器是Quartz的核心组件,它负责管理所有的Job和Trigger,以及它们的执行计划。通过Scheduler实例,你可以注册Job和Trigger,启动、暂停、恢复或删除它们。 3. **持久化...

    quartz-1.6.0任务调试

    在"quartz-1.6.0任务调试"中,我们需要理解Quartz的基本概念、配置以及如何进行任务的调试。 首先,Quartz的核心是Job和Trigger。Job是实际需要执行的任务,而Trigger则是触发Job执行的时间规则。在Quartz 1.6.0...

    quartz-2.4.0.zip

    1. **API文档**:Quartz提供了详尽的Javadoc,帮助开发者了解如何创建、配置和管理Job以及Trigger。 2. **源代码**:对于学习和调试非常有用,可以深入理解其内部机制。 3. **示例**:通常会有一系列的示例项目,...

    quartz-2.1.7.gz

    1. **并发控制**:Quartz允许设置Job的并发策略,如单例Job(同一时间只有一个实例运行)或多实例Job(允许多个实例同时运行)。 2. **触发器绑定**:一个Job可以有多个Trigger,允许根据不同的条件执行。 3. **...

    Quartz2.2.1深入Job、JobDetail、JobDataMap、Trigger

    Quartz2.2.1深入Job、JobDetail、JobDataMap、Trigger ,具体效果和过程看博文:http://blog.csdn.net/evankaka/article/details/45361759

    quartz-2.2 以及关联jar包

    1. **Job**:在Quartz中,Job代表一个需要执行的任务,它是一个接口,开发者需要实现这个接口来定义具体的业务逻辑。 2. **Trigger**:Trigger是触发Job执行的时间规则,它可以是简单的定时器,也可以是复杂的...

    Spring Quartz定时器的jar包

    2. 配置Spring的ApplicationContext,声明Quartz相关的bean,如SchedulerFactoryBean、JobDetail和Trigger。 3. 定义Job类,实现`org.quartz.Job`接口,定义任务的具体逻辑。 4. 创建Trigger,指定Job的执行策略,...

    quartz-2.1.7 官方jar包源码

    1. `org.quartz.core` 包:这是Quartz的核心模块,包含Scheduler、JobStore和ThreadPool的主要实现。SchedulerImpl是调度器的实现,它负责管理和调度Jobs。JobStore是用于存储Jobs和Triggers的地方,常见的实现有...

Global site tag (gtag.js) - Google Analytics