通过第一篇文章的学习,已经初步认识了定时任务,如果说想要在执行任务时,将一些动态的值,传递给execute方法该怎么处理呢?
JobDataMap
JobDataMap可用于保存任何数量的(可序列化的)数据对象,您希望在执行时可以将其提供给作业实例。JobDataMap是Java Map接口的一个实现,并且有一些用于存储和检索原始类型的数据的方便方法。
改造之后的测试方法如下调用,用的是usingJobData方法
// 具体任务 JobDetail JobDetail job = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") .usingJobData("jobSays", "Hello World!") .usingJobData("myFloatValue", 3.141f) .build();
HelloJob如下
/** * 具体执行的任务 */ public class HelloJob 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); } }
此时在运行结果如下
Instance group1.job1 of DumbJob says: Hello World!, and val is: 3.141 Instance group1.job1 of DumbJob says: Hello World!, and val is: 3.141
如果在Job类中定义与JobDataMap中键值一致的set和get方法,那么Quartz会自动将这些属性注入。如:
// 具体任务 JobDetail JobDetail job = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") .usingJobData("name", "安森") .usingJobData("age", 24).build();
helloJob
package com.anson.example2; import org.quartz.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 具体执行的任务 */ public class HelloJob implements Job { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public void execute(JobExecutionContext context) throws JobExecutionException { JobKey key = context.getJobDetail().getKey(); System.out.println("Instance " + key + ",姓名:" + name + ",年龄:" + age); } }
结果
Instance group1.job1,姓名:安森,年龄:24 Instance group1.job1,姓名:安森,年龄:24 Instance group1.job1,姓名:安森,年龄:24
另外Trigger中也可以设置JobDataMap属性,这是为了在多个Trigger中使用相同的Job。JobExecutionContext 将会合并JobDetail与Trigger的JobDataMap,如果其中属性名相同,后者将覆盖前者。可以使用JobExecutionContext.getMergedJobDataMap()方法来获取合并后的JobDataMap。
Xml 配置方式如何传递?
在spring中,如果HelloJob的方法有参数,那么需要指定一些设定才可以,否则会在运行时有NoSuchMethodException异常发生。
简单示例
public class HelloJob { public void execute(String args){ System.out.println("------ " + args + "------ "); } }
xml配置
<!-- 配置JobDetail --> <bean id="springQtzJobMethod" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <!-- 执行目标job --> <property name="targetObject" ref="helloJob"></property> <!-- 要执行的方法 --> <property name="targetMethod" value="execute"></property> <!-- 设置参数--> <property name="arguments" value="安森"></property> </bean>
增加一个arguments的变量,设置一个value即可,如果你点进去观察源代码,会发现,它其实是一个 Object[] 类型的参数
执行结果
------ 安森------ ------ 安森------
如果你想传递2个参数,示例如下
public void execute(String name, int age) { System.out.println("------ " + name + ":" + age + "------ "); }
<!-- 设置参数--> <property name="arguments"> <list> <value>安森</value> <value>23</value> </list> </property>
当然,你也可以传递Map,Set,JavaBean对象等等,这里只是抛砖引玉。
以下为Quartz提供的官方示例
示例演示了静态变量和非静态变量的修改,只有静态成员才能改变
import org.quartz.*; import java.text.SimpleDateFormat; import java.util.Date; @PersistJobDataAfterExecution @DisallowConcurrentExecution public class ColorJob implements Job { public static final String FAVORITE_COLOR = "favorite color"; public static final String EXECUTION_COUNT = "count"; // 由于Quartz会在每次执行时重新实例化一个类,因此成员非静态成员变量不能用于维护状态! private int _counter = 1; public void execute(JobExecutionContext context) throws JobExecutionException { JobKey jobKey = context.getJobDetail().getKey(); JobDataMap data = context.getJobDetail().getJobDataMap(); String favoriteColor = data.getString(FAVORITE_COLOR); int count = data.getInt(EXECUTION_COUNT); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); System.out.println("任务Key: " + jobKey + " ,执行时间: " + sdf.format(new Date()) + "\n" + " 传递参数(favorite color): " + favoriteColor + "\n" + " 传递参数(count): " + count + "\n" + " ColorJob非静态变量值: " + _counter + "\n"); count++; data.put(EXECUTION_COUNT, count); data.put(FAVORITE_COLOR, "黄色"); _counter++; } }
package com.anson.examples.example4; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.text.SimpleDateFormat; import java.util.Date; import static org.quartz.DateBuilder.nextGivenSecondDate; import static org.quartz.JobBuilder.newJob; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import static org.quartz.TriggerBuilder.newTrigger; public class JobStateExample { public void run() throws Exception { Logger log = LoggerFactory.getLogger(JobStateExample.class); SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); //在当前时间10秒后运行 Date startTime = nextGivenSecondDate(null, 10); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); //通过过JobDetail封装ColorJob,同时指定Job在Scheduler中所属组及名称,这里,组名为group1,而名称为job1。 JobDetail job1 = newJob(ColorJob.class) .withIdentity("job1", "group1") .build(); // 创建一个SimpleTrigger实例,指定该Trigger在Scheduler中所属组及名称。 // 接着设置调度的时间规则.当前时间10秒后运行,每10秒运行一次,共运行4次 SimpleTrigger trigger1 = newTrigger() .withIdentity("trigger1", "group1") .startAt(startTime) .withSchedule(simpleSchedule() .withIntervalInSeconds(10) .withRepeatCount(4)) .build(); //将参数传递入任务的数据Map中 job1.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "绿色"); job1.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1); //注册并进行调度 Date scheduleTime1 = sched.scheduleJob(job1, trigger1); log.error("任务key: " + job1.getKey() + ",执行运行时间: " + sdf.format(scheduleTime1) + ",触发器重复执行次数: " + trigger1.getRepeatCount() + ",触发器执行时间: " + trigger1.getRepeatInterval() / 1000 + "秒"); //第二个任务 JobDetail job2 = newJob(ColorJob.class) .withIdentity("job2", "group1") .build(); SimpleTrigger trigger2 = newTrigger() .withIdentity("trigger2", "group1") .startAt(startTime) .withSchedule(simpleSchedule() .withIntervalInSeconds(10) .withRepeatCount(4)) .build(); //传递数据 job2.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "红色"); job2.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1); Date scheduleTime2 = sched.scheduleJob(job2, trigger2); log.error("第二个任务key: " + job2.getKey().toString() + ",执行运行时间: " + sdf.format(scheduleTime2) + ",触发器重复执行次数: " + trigger2.getRepeatCount() + ",触发器执行时间: " + trigger2.getRepeatInterval() / 1000 + "秒"); //调度器启动 sched.start(); try { Thread.sleep(60L * 1000L); } catch (Exception e) { } //调度器停止 sched.shutdown(true); SchedulerMetaData metaData = sched.getMetaData(); log.error("Executed " + metaData.getNumberOfJobsExecuted() + " jobs."); } public static void main(String[] args) throws Exception { JobStateExample example = new JobStateExample(); example.run(); } }
执行结果如下
[INFO] 11 九月 03:04:13.742 下午 main [com.anson.examples.example4.JobStateExample] 任务key: group1.job1,执行运行时间: 2017-09-11 03:04:20,触发器重复执行次数: 4,触发器执行时间: 10秒 [INFO] 11 九月 03:04:13.742 下午 main [com.anson.examples.example4.JobStateExample] 第二个任务key: group1.job2,执行运行时间: 2017-09-11 03:04:20,触发器重复执行次数: 4,触发器执行时间: 10秒 [INFO] 11 九月 03:04:13.742 下午 main [org.quartz.core.QuartzScheduler] Scheduler MyScheduler_$_NON_CLUSTERED started. 任务Key: group1.job2 ,执行时间: 2017-09-11 03:04:20 传递参数(favorite color): 红色 传递参数(count): 1 ColorJob非静态变量值: 1 任务Key: group1.job1 ,执行时间: 2017-09-11 03:04:20 传递参数(favorite color): 绿色 传递参数(count): 1 ColorJob非静态变量值: 1 任务Key: group1.job1 ,执行时间: 2017-09-11 03:04:30 传递参数(favorite color): 黄色 传递参数(count): 2 ColorJob非静态变量值: 1 任务Key: group1.job2 ,执行时间: 2017-09-11 03:04:30 传递参数(favorite color): 黄色 传递参数(count): 2 ColorJob非静态变量值: 1 任务Key: group1.job1 ,执行时间: 2017-09-11 03:04:40 传递参数(favorite color): 黄色 传递参数(count): 3 ColorJob非静态变量值: 1 任务Key: group1.job2 ,执行时间: 2017-09-11 03:04:40 传递参数(favorite color): 黄色 传递参数(count): 3 ColorJob非静态变量值: 1 任务Key: group1.job1 ,执行时间: 2017-09-11 03:04:50 传递参数(favorite color): 黄色 传递参数(count): 4 ColorJob非静态变量值: 1 任务Key: group1.job2 ,执行时间: 2017-09-11 03:04:50 传递参数(favorite color): 黄色 传递参数(count): 4 ColorJob非静态变量值: 1 任务Key: group1.job1 ,执行时间: 2017-09-11 03:05:00 传递参数(favorite color): 黄色 传递参数(count): 5 ColorJob非静态变量值: 1 任务Key: group1.job2 ,执行时间: 2017-09-11 03:05:00 传递参数(favorite color): 黄色 传递参数(count): 5 ColorJob非静态变量值: 1 [INFO] 11 九月 03:05:13.747 下午 main [org.quartz.core.QuartzScheduler] Scheduler MyScheduler_$_NON_CLUSTERED shutting down. [INFO] 11 九月 03:05:13.747 下午 main [org.quartz.core.QuartzScheduler] Scheduler MyScheduler_$_NON_CLUSTERED paused. [INFO] 11 九月 03:05:14.142 下午 main [org.quartz.core.QuartzScheduler] Scheduler MyScheduler_$_NON_CLUSTERED shutdown complete. [INFO] 11 九月 03:05:14.142 下午 main [com.anson.examples.example4.JobStateExample] Executed 10 jobs.
Job的状态与并发
@DisallowConcurrentExecution:同一时间将只有一个Job实例被执行。
@PersistJobDataAfterExecution:在Job被执行结束后,将会更新JobDataMap,这样下次Job执行后就会使用新的值而不是初始值。 上面的示例中,如果不用此注解,成员变量的值下次调用也不会有改变。
如果使用@PersistJobDataAfterExecution注解,推荐也使用@DisallowConcurrentExecution注解,这是为了避免并发问题导致数据紊乱。
其它属性
Durability,持久性;如果Job是非持久性的,一旦没有Trigger与其相关联,它就会从Scheduler中被删除。也就是说Job的生命周期和其Trigger是关联的。
RequestsRecovery,如果为true,那么在Scheduler异常中止或者系统异常关闭后,当Scheduler重启后,Job会被重新执行。
JobExecutionException
execute()方法只允许抛出JobExecutionException异常