所有的配置都在xml中完成,包括cronExpression表达式,十分的方便。但是如果我的任务信息是保存在数据库的,想要动态的初始化,而且任务较多的时候不是得有一大堆的xml配置?或者说我要修改一下trigger的表达式,使原来5秒运行一次的任务变成10秒运行一次,这时问题就来了,试过在配置文件中不传入cronExpression等参数,但是启动时就报错了,难道我每次都修改xml文件然后重启应用吗,这显然不合适的。最理想的是在与spring整合的同时又能实现动态任务的添加、删除及修改配置。
我们来看一下spring实现quartz的方式,先看一下上面配置文件中定义的jobDetail。其实上面生成的jobDetail并不是我们定义的Bean,因为在Quartz 2.x版本中JobDetail已经是一个接口(当然以前的版本也并非直接生成JobDetail):
public interface JobDetail extends Serializable, Cloneable {…}
Spring是通过将其转换为MethodInvokingJob或StatefulMethodInvokingJob类型来实现的,这两个都是静态的内部类,MethodInvokingJob类继承于QuartzJobBean,而StatefulMethodInvokingJob则直接继承于MethodInvokingJob。 这两个类的实现区别在于有状态和无状态,对应于quartz的Job和StatefulJob,具体可以查看quartz文档,这里不再赘述。先来看一下它们实现的QuartzJobBean的主要代码:
/**
* This implementation applies the passed-in job data map as bean property
* values, and delegates to <code>executeInternal</code> afterwards.
* @see #executeInternal
*/
public final void execute(JobExecutionContext context)
throws JobExecutionException {
try {
// Reflectively adapting to differences between Quartz 1.x and Quartz 2.0...
Scheduler scheduler = (Scheduler) ReflectionUtils.invokeMethod(getSchedulerMethod, context);
Map mergedJobDataMap = (Map) ReflectionUtils.invokeMethod(getMergedJobDataMapMethod, context);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(
this);
MutablePropertyValues pvs =
new MutablePropertyValues();
pvs.addPropertyValues(scheduler.getContext());
pvs.addPropertyValues(mergedJobDataMap);
bw.setPropertyValues(pvs,
true);
}
catch (SchedulerException ex) {
throw new JobExecutionException(ex);
}
executeInternal(context);
}
/**
* Execute the actual job. The job data map will already have been
* applied as bean property values by execute. The contract is
* exactly the same as for the standard Quartz execute method.
* @see #execute
*/
protected abstract void executeInternal(JobExecutionContext context)
throws JobExecutionException;
//还有MethodInvokingJobDetailFactoryBean中的代码:
public void afterPropertiesSet()
throws ClassNotFoundException, NoSuchMethodException {
prepare();
// Use specific name if given, else fall back to bean name.
String name = (
this.name !=
null ?
this.name :
this.beanName);
// Consider the concurrent flag to choose between stateful and stateless job.
Class jobClass = (
this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);
// Build JobDetail instance.
if (jobDetailImplClass !=
null) {
// Using Quartz 2.0 JobDetailImpl class...
this.jobDetail = (JobDetail) BeanUtils.instantiate(jobDetailImplClass);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(
this.jobDetail);
bw.setPropertyValue(
"name", name);
bw.setPropertyValue(
"group",
this.group);
bw.setPropertyValue(
"jobClass", jobClass);
bw.setPropertyValue(
"durability",
true);
((JobDataMap) bw.getPropertyValue(
"jobDataMap")).put(
"methodInvoker",
this);
}
else {
// Using Quartz 1.x JobDetail class...
this.jobDetail =
new JobDetail(name,
this.group, jobClass);
this.jobDetail.setVolatility(
true);
this.jobDetail.setDurability(
true);
this.jobDetail.getJobDataMap().put(
"methodInvoker",
this);
}
// Register job listener names.
if (
this.jobListenerNames !=
null) {
for (String jobListenerName :
this.jobListenerNames) {
if (jobDetailImplClass !=
null) {
throw new IllegalStateException(
"Non-global JobListeners not supported on Quartz 2 - " +
"manually register a Matcher against the Quartz ListenerManager instead");
}
this.jobDetail.addJobListener(jobListenerName);
}
}
postProcessJobDetail(
this.jobDetail);
}