讲解的Jobs和JobDetail

发表时间:2018-01-11 16:01:02 浏览量( 80 ) 留言数( 0 )

学习目标:

1、深入了解Jobs和JobDetail的环境搭建


学习过程:

   Jobs的实现类非常简单,只需要实现Job接口,然后重写execute方法即可,然后通过JobBuilder建立一个JobDetail实例对象,把Job的实现类传入JobDetail,同时定义其名字name和组group。然后就可以把改JobDetail交给scheduler,scheduler在执行job时,是会实例化一个Job对象的,每一次执行都会实例化一个Job对象,这里有些地方需要注意的是:Job的实现类必须有一个无参的构造方法,否则在实例化Job对象时会报错的,还有就是我们不要设置一些保存数据状态的属性到Job实现类中,因为scheduler每执行一次都是实例化一个Job对象出来。

    那这样可能就会有一个问题,就是我如何可以给一个Job传递一些参数呢?我们可以通过JobDataMap实现这个功能的。

一、JobDataMap的作用

   我们客户可以通过JobDataMap把一些数据或者对象(注意必须要时可序列化的)传递给即将运行的Job对象。

1、job实现类

public class HelloJob implements Job {

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

	}

}


2、运行类

public class QuartzTest {

	public static void main(String[] args) {

		try {
			SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();

			Scheduler sched = schedFact.getScheduler();

			sched.start();

			// define the job and tie it to our HelloJob class
			JobDetail job = newJob(HelloJob.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
			sched.scheduleJob(job, trigger);
		} catch (SchedulerException se) {
			se.printStackTrace();
		}
	}
}


输出结果:

attcontent/e11f23c6-e184-4641-a8f7-7c1a7a61eec0.png

   这里面我们在补充一点,传入的数据如果时对象,如果你的任务也配置了持久化的,那么传入的对象一定要是可序列化的,因为对象会序列化保存到数据库中,如果不能序列化是会报异常的。

   除了通过在execute中通过get方法获得JobDataMap的数据之外,你也可以通过设置同名的set方法可以直接获得。

public class HelloJob2 implements Job {
	
	private String jobSays ;
	private float myFloatValue ;

	public void execute(JobExecutionContext context) throws JobExecutionException {

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


		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) {
		this.myFloatValue = myFloatValue;
	}

	
}


二、并发和保存状态

@DisallowConcurrentExecution

JobDataMap还有一个非常重要的功能,就是保存Job的状态,防止一个Job在同一时间执行了两次。因为Job是有可能并发执行的,比如一个任务要执行10秒中,而调度算法是每秒中触发1次,那么就有可能多个任务被并发执行。当然这个功能并不需要我们去做,我们只需要使用@DisallowConcurrentExecution注解就可以了。当然这个方法自己可以根据需要使用。


@PersistJobDataAfterExecution  

如果你想在 某个job执行的时候传入参数,参数在job执行过程中对参数有所修改,并且在job执行完毕后把参数返回。一般来说使用PersistJobDataAfterExecution的就会使用DisallowConcurrentExecution。通过设置这个之后呢,Job就可以保存一些状态属性了。

看一下下面的代码:先不加任何注解,这个任务要10秒才能完成

public class StateJob implements Job {
	
	private int count ;

	public void execute(JobExecutionContext context) throws JobExecutionException {
		JobKey key = context.getJobDetail().getKey();
		JobDataMap data = context.getJobDetail().getJobDataMap(); 
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println("key:"+key+",count="+count+",date="+new Date());
		count++;//加一
		data.put("count", count);//重新放回JobDataMap
	}

	public void setCount(int count) {
		this.count = count;
	}

}

看一下运行类,这个任务3秒就执行一次了。

public class QuartzTest {

	public static void main(String[] args) {

		try {
			SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();

			Scheduler sched = schedFact.getScheduler();

			sched.start();

			// define the job and tie it to our HelloJob class
			JobDetail job = newJob(StateJob.class).withIdentity("StateJobName1", "StateJobgroup1")
					.usingJobData("count", 1).build();

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

			// Tell quartz to schedule the job using our trigger
			sched.scheduleJob(job, trigger);
		} catch (SchedulerException se) {
			se.printStackTrace();
		}
	}
}

看一下输出,count的状态并没有保存,每一次都是1,而且输出的时间也是每3秒就会输出一次,明显前一个任务还没有执行完成,就执行了一个新任务了。

attcontent/5d0d0714-b33d-4b48-b938-1aa996f18308.png

我们可以先加上

@DisallowConcurrentExecution 看一下输出的结果,变成了每10秒输出一次,也就是如果有任务没有完成,不会启动新的任务的。但是count还是一直都是1而已。

attcontent/8ed77a5f-dc38-49d6-a769-1e26a60635f0.png

同时加上@PersistJobDataAfterExecution

再看一下输出。

attcontent/7a41e73f-0347-40af-a77a-200634c47568.png


当触发器被触发的时候,通过Scheduler中配置的JobFactory来实例化与之关联的Job类。  缺省的JobFactory只是简单地对Job类调用newInstance()方法。 

创建自己JobFactory可以利用应用中诸如Ioc或者DI容器所产生或者初始化的Job实例。


这里简短地总结一下通过JobDetail对象可以定义Job的其它属性。

Durability(持久性)-如果一个Job是不持久的, 一旦没有触发器与之关联,它就会被从scheduler 中自动删除。  

RequestsRecovery(请求恢复能力) -如果一个Job具备“请求恢复”能力,当它在执行时遇到scheduler “硬性的关闭”(例如:执行的过程崩溃,或者计算机被关机),那么当scheduler重新启动时,这个任务会被重新执行。

这种情况下,JobExecutionContext.isRecovering() 属性将是true。

代码如下:

JobDetail job = newJob(StateJob.class).withIdentity("StateJobName1", "StateJobgroup1").storeDurably(true)
	.requestRecovery(true).usingJobData("count", 1).build();