QuartZ和Spring整合

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>
    


cornExpression 具体请查看  表达式示例

下面是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>


在这里配置了一系列参数,在类中有一个静态的子类继承了Job,真正触发的是它,让我们来看一下它的逻辑.

主要注意的是,该类必须要继承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