日志:
Quartz使用SLF4J框架来满足所有的日志记录需求
SchedulerFactory
quartz中SchedulerFactory实现类:
org.quartz.impl.StdSchedulerFactory
org.quartz.impl.DirectSchedulerFactory
StdSchedulerFactory:
创建和初始化:
1.配置(java.util.Properties属性完成->配置文件中加载)
2.程序创建并直接传递到工厂
DirectSchedulerFactory:
不支持配置完成初始化,程序编辑所有调度程序的设置,要求用户了解全过程(不支持)
Job
quartz中定义job都要实现org.quartz.Job接口,并在execute方法中定义job的执行流程
public class HelloJob implements Job{
private static Logger logger = LoggerFactory.getLogger(HelloJob.class);
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.info("hello world!!!");
}
}
JobDetail
然而我们有时还需获取job相关属性,根据job属性来定义执行流程,此时我们则可以使用JobExecutionContext 对象来获取,如通过getJobDetail()来获取预先定义的job属性
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.info("hello world!!!");
JobDetail jobDetail=jobExecutionContext.getJobDetail();
JobDataMap jobDataMap=jobDetail.getJobDataMap();
String name=jobDataMap.getString("name");
String sex=jobDataMap.getString("sex");
Double height=jobDataMap.getDouble("height");
//取出自定义对象
Hero hero=(Hero) jobDataMap.get("hero");
int age=jobDataMap.getIntValue("age");
logger.info("name:"+name+"\tsex:"+sex+"\tage:"+age+"\theight:"+height);
logger.info("hero:");
logger.info(hero.toString());
}
JobDataMap
在上面的案例中我们用到了JobDetail中的JobDataMap,下面演示如何利用JobDataMap向jobExecutionContext传递参数
public static JobDetail defineJobDetailAndTieToRealJobClass(){
//存入JobDataMap(合并使用)
JobDataMap newJobDataMap=new JobDataMap();
newJobDataMap.put("name","关羽");
newJobDataMap.put("age",89);
newJobDataMap.put("height",2.8D);
//通过自己创建JobDataMap存入自定义对象
Hero hero=new Hero("张飞","男",45,1.89D);
newJobDataMap.put("hero",hero);
JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1")
.usingJobData("name","刘备")
.usingJobData("age",54)
.usingJobData("sex","男")
.usingJobData(newJobDataMap)//putAll合并JobDataMap
.build();
return job;
}
执行结果:
[INFO] 26 五月 06:02:00.010 下午 DefaultQuartzScheduler_Worker-1 [com.feifei.test1.HelloJob]
hello world!!!
[INFO] 26 五月 06:02:00.012 下午 DefaultQuartzScheduler_Worker-1 [com.feifei.test1.HelloJob]
name:关羽 sex:男 age:89 height:2.8
[INFO] 26 五月 06:02:00.012 下午 DefaultQuartzScheduler_Worker-1 [com.feifei.test1.HelloJob]
hero:
[INFO] 26 五月 06:02:00.012 下午 DefaultQuartzScheduler_Worker-1 [com.feifei.test1.HelloJob]
Hero{name='张飞', sex='男', age=45, height=1.89}
自定义对象(考虑序列化问题实现Serializable接口):
public class Hero implements Serializable{
private String name;
private String sex;
private int age;
private Double height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Double getHeight() {
return height;
}
public void setHeight(Double height) {
this.height = height;
}
public Hero() {
}
public Hero(String name, String sex, int age, Double height) {
this.name = name;
this.sex = sex;
this.age = age;
this.height = height;
}
@Override
public String toString() {
return "Hero{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", height=" + height +
'}';
}
}
JobDataMap中可以包含不限量的(序列化的)数据对象,JobDataMap是Java Map接口的一个实现,只是额外增加了一些便于存取基本类型的数据的方法来减少存储取出时的强制类型转换。如下:
putAsString(String,boolean):void
getIntValue(String):int
getDoubleValue(String):Double
getFloatValue(String):Float
getLongValue(String):Long
相关的方法还有很多,这里只是列举出一部分。
如果你在JobDataMap中存储的都是java标准类型时并不用担心什么。
如果JobDataMap中存储的有自定义对象,而你又恰好使用持久化的存储机制,那么你就要注意了:
JobDataMap中存储的对象都会被序列化,如果你已经有了一个类序列化后的实例,在那之后有人改变了该类的定义,则有可能因破坏了兼容性而产生异常。
持久化的存储机制(JobStore中说明)
简单解决(如何配置以后说明):
配置JDBC-JobStore和JobDataMap只允许存储基本类型和String类型的数据
经过上面的学习我们已经能够向execute中传递和使用参数了,现在我们再思考一些细节问题:
我们可以通过JobDetail来设置job属性,我们为什么不能直接使用job实现类的属性呢,定义Job实现类时有什么要注意的呢?
scheduler在执行job的execute(…)之前,首先会调用HelloJob的newInstance()创建一个新的实例,调用完成后,HelloJob实例引用就被丢弃了,接着被垃圾回收,可知
1.HelloJob必须有一个无参的构造函数(当使用默认的JobFactory时);
2.job类中,不应该定义有状态的数据属性,因为在job的多次执行中,这些属性的值不会保留。(每次执行会重新创建一个HelloJob实例,之前定义的数据都会丢失)
我们已经学会用JobDetail中的JobDataMap来存入和取出数据了,但每次都一个一个的根据key取出,有什么简便方法吗?
使用JobFactory实现数据的自动注入是个不错的选择,他能让你的excute()方法变的更加简洁,只要在HelloJob中添加对应的自定义属性和set方法即可,JobFactory会帮你完成余下的工作的,HelloJob修改如下:
public class HelloJob implements Job{
private static Logger logger = LoggerFactory.getLogger(HelloJob.class);
private Hero hero;
public static Logger getLogger() {
return logger;
}
public static void setLogger(Logger logger) {
HelloJob.logger = logger;
}
public Hero getHero() {
return hero;
}
public void setHero(Hero hero) {
this.hero = hero;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Double getHeight() {
return height;
}
public void setHeight(Double height) {
this.height = height;
}
private String name;
private String sex;
private int age;
private Double height;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
logger.info("hello world!!!");
logger.info("name:"+name+"\tsex:"+sex+"\tage:"+age+"\theight:"+height);
logger.info("hero:");
logger.info(hero.toString());
}
}
相关注解:
@DisallowConcurrentExecution
位置:HelloJob类
作用范围:JobDetail对象
作用效果:不允许JobDetail对象并发运行
@PersistJobDataAfterExecution
位置:HelloJob类
作用范围:JobDetail对象
作用效果:成功执行了job类的execute方法后(没有发生任何异常),更新JobDetail中JobDataMap的数据,jobDetail在下一次执行时获取到新的数据.
JobDetail配置更多属性:
Durability:如果一个job是非持久的,当没有活跃的trigger与之关联的时候,会被自动地从scheduler中删除。也就是说,非持久的job的生命期是由trigger的存在与否决定的;
RequestsRecovery:如果一个job是可恢复的,并且在其执行的时候,scheduler发生硬关闭(hard shutdown)(比如运行的进程崩溃了,或者关机了),则当scheduler重新启动的时候,该job会被重新执行。此时,该job的JobExecutionContext.isRecovering() 返回true。
JobExecutionException
细心的朋友应该已经发现了Job.execute()方法上声明了一个运行时异常->
JobExecutionException,Job通过该异常通知scheduler,这个异常携带着很多信息,你也许该花点时间看看JobExecutionException的文档,以便更好的处理此类异常。
下一篇我们开始Trigger,未完待续...