Spring 版本: 2.5
QuartZ 版本 1.6.6
Spring整合QuartZ算是非常简单的.
创建一个Bean即可.
<bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref ="dataSource" />
<property name="applicationContextSchedulerContextKey" value="applicationContextKey"/>
<!--这个是必须的,QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动-->
<property name="startupDelay" value="30"/>
<!--这个是可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了-->
<property name="overwriteExistingJobs" value="true"/>
<property name="configLocation" value="classpath:timer.properties"/>
</bean>
timer.properties中的内容,就是 QuartZ快速入门中的内容
定义一个服务,把调度器注入进去
<bean id="schedulingService" class="xxx.SchedulingServiceImpl">
<property name="scheduler" ref ="quartzScheduler" />
</bean>
下面是一个实现的调度器例子:定时上下架
定时上下架的class
public class ProdUpAndDownImpl {
private SchedulingService schedulingService_;
private String cronExpression_;
public void setSchedulingService(SchedulingService schedulingService) {
this.schedulingService_ = schedulingService;
}
public void setCronExpression(String cronExpression) {
this.cronExpression_ = cronExpression;
}
public void init() {
schedulingService_.scheduleJob("prodUpdownTask", null, "prodUpdownTask", cronExpression_);
}
}
通过bean注入参数
<!-- 定时上下架 -->
<bean id="prodUpdownListener" class="xxx.ProdUpAndDownImpl" init-method="init">
<property name="schedulingService" ref ="schedulingService" />
<property name="cronExpression" value ="0 0/15 * * * ?" />
</bean>
下面是Service具体的调用
public void scheduleJob(String jobName, Object[] arguments, String triggerName, String cronExpression) throws SchedulingException {
scheduleJob(jobName, arguments, triggerName, cronExpression, Scheduler.DEFAULT_GROUP);
}
/**
* 根据 Quartz Cron Expression 调度任务
* @param jobName 调度任务需要执行的服务
* @param arguments 任务参数(必须可序列化)
* @param triggerName 触发器名称
* @param cronExpression Quartz Cron 表达式,如 "0/10 * * ? * * *"等
* @param triggerGroup 触发器组名称
*/
public void scheduleJob(String jobName, Object[] arguments, String triggerName, String cronExpression, String triggerGroup) {
JobDetail jobDetail = (JobDetail) context_.getBean(jobName);
if (arguments != null) {
jobDetail.getJobDataMap().put("arguments", arguments);
}
// 使用TriggerName作为作业名
jobDetail.setName(triggerName);
// 调度器增加一个job,true表示替换.
scheduler_.addJob(jobDetail, true);
// 具体触发
CronTrigger cronTrigger = new CronTrigger(triggerName, triggerGroup, jobDetail.getName(),
Scheduler.DEFAULT_GROUP);
// 触发的表达式
cronTrigger.setCronExpression(cronExpression);
// 获取这个触发实例
if (scheduler_.getTrigger(triggerName, triggerGroup) == null) {
// 给触发器添加标示
scheduler_.scheduleJob(cronTrigger);
} else {
// 重置对应的触发器.
scheduler_.rescheduleJob(triggerName, triggerGroup, cronTrigger);
}
}
下面是具体的任务类 prodUpdownTask的Bean
<!-- 定时上下架 -->
<bean id="prodUpdownTask" class="xxx.MethodInvokingJobDetailFactoryBean">
<property name="shouldRecover" value="true"/>
<property name="concurrent" value="false"/>
<property name="targetService" value="prodUpdownServiceSchedule"/>
<property name="targetMethod" value="updateProdUpdown"/>
</bean>
主要注意的是,该类必须要继承FactoryBean, BeanNameAware, InitializingBean三个接口.
FactoryBean接口的作用,
通过applicationContext获取的对象不是该bean,而是getObject()对象返回的值.所以在service中通过
context_.getBean(className);获取到得是JobDetail.
BeanNameAware的作用:
如果某个 bean 需要访问配置文件中本身的 id 属性,则可以使用 BeanNameAware 接口,
该接口提供了回调本身的能力。实现该接口的 bean,能访问到本身的 id 属性。
InitializingBean的作用:
在Bean被初始化后调用afterPropertiesSet 方法,在该方法中,我们需要进行jobDetail的详细配置.
大致包括如下:
jobDetail = new JobDetail();
jobDetail.setName(beanName);
jobDetail.setGroup(group);
jobDetail.setJobClass(concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);
jobDetail.setDurability(durable);
jobDetail.setVolatility(volatility);
jobDetail.setRequestsRecovery(shouldRecover);
if (targetClass != null)
jobDetail.getJobDataMap().put("targetClass", targetClass);
if (targetService != null)
jobDetail.getJobDataMap().put("targetService", targetService);
if (targetObject != null)
jobDetail.getJobDataMap().put("targetObject", targetObject);
if (targetMethod != null)
jobDetail.getJobDataMap().put("targetMethod", targetMethod);
if (staticMethod != null)
jobDetail.getJobDataMap().put("staticMethod", staticMethod);
if (arguments != null)
jobDetail.getJobDataMap().put("arguments", arguments);
logger.debug("Registering JobListener names with JobDetail object " + beanName);
if (this.jobListenerNames != null) {
for (int i = 0; i < this.jobListenerNames.length; i++) {
this.jobDetail.addJobListener(this.jobListenerNames[i]);
}
}
静态类,继承Job,后会有一个execute的方法.
public void execute(JobExecutionContext context) throws JobExecutionException {
String targetClass = context.getMergedJobDataMap().getString("targetClass");
Class targetClassClass = null;
if (targetClass != null) {
targetClassClass = Class.forName(targetClass); // Could
// throw
// ClassNotFoundException
}
Object targetObject = context.getMergedJobDataMap().get("targetObject");
String targetService = context.getMergedJobDataMap().getString("targetService");
String targetMethod = context.getMergedJobDataMap().getString("targetMethod");
String staticMethod = context.getMergedJobDataMap().getString("staticMethod");
Object[] arguments = (Object[]) context.getMergedJobDataMap().get("arguments");
//方法调用, 通过放射. 目标类,目标对象,目标方法,目标参数,准备,执行
MethodInvoker methodInvoker = new MethodInvoker();
methodInvoker.setTargetClass(targetClassClass);
if (targetObject == null) {
targetObject = WebUtils.getBean(targetService);
}
methodInvoker.setTargetObject(targetObject);
methodInvoker.setTargetMethod(targetMethod);
methodInvoker.setStaticMethod(staticMethod);
methodInvoker.setArguments(arguments);
methodInvoker.prepare();
methodInvoker.invoke();
}
具体API可以查看 Spring2.5API