一直以来都认为spring对quartz的支持不是特别合理,每一个定时任务需要配置多达三个bean,一个targetObject,一个Jobdetail,一个trigger。今天自己实现了一个简单的spring对quartz的支持,一个任务只需要配置一个spring bean即可,bean仅有一个必要属性,cronExpression(触发条件表达式)。
先贴出核心类
package com.crunii.emall.quartz;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.BeanNameAware;
public abstract class QuartzJob extends JobDetail implements Job, BeanNameAware {
private String beanName;
public void execute(JobExecutionContext jobexecutioncontext) throws JobExecutionException {
run();
}
private String cronExpression;
protected Log log = LogFactory.getLog(this.getClass());
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
public abstract void run();
public void setBeanName(String beanName) {
super.setGroup(beanName);
super.setName(beanName);
super.setJobClass(this.getClass());
this.beanName = beanName;
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE).append("cronExpression", this.cronExpression)
.toString();
}
public String getBeanName() {
return beanName;
}
}
每一个需要定时执行的任务都要继承这个类,run方法里面是具体的业务代码
package com.crunii.emall.quartz;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.SchedulerException;
import org.quartz.simpl.SimpleJobFactory;
import org.quartz.spi.TriggerFiredBundle;
public class SpringBeanJobFactory extends SimpleJobFactory {
@SuppressWarnings("unchecked")
public Job newJob(TriggerFiredBundle triggerfiredbundle) throws SchedulerException {
JobDetail jobDetail = triggerfiredbundle.getJobDetail();
if (jobDetail instanceof QuartzJob && jobDetail instanceof Job) {
return (Job) jobDetail;
} else {
return super.newJob(triggerfiredbundle);
}
}
}
这个东西困扰了我很久,quartz在定制任务的时候,默认是自动把jobdetail里面的class做newinstance,个人觉得这个做法不太合理,应该开放一个api来实现可以放入具体的Object,而不是class。因为有的时候我需要自己来生成需要执行的object,比如在和spring集成的时候,我就需要将spring里面的bean直接放入到quartz工厂,而quartz显然不支持这个做法。
下面从spring工厂里面取得需要定时执行的object,放入到quartz工厂
try {
SchedulerFactory schedFact = new StdSchedulerFactory();
sched = schedFact.getScheduler();
// 这里需要重写jobfactory,不然springbean放入进去之后,所有spring注入的属性都不能用,nullpointer
SpringBeanJobFactory jobfactory = new SpringBeanJobFactory();
sched.setJobFactory(jobfactory);
sched.start();
Map<String, QuartzJob> quartzTasks = context.getBeansOfType(QuartzJob.class);
for (QuartzJob quartzJob : quartzTasks.values()) {
String beanName = quartzJob.getBeanName();
CronTrigger trigger = new CronTrigger(beanName, beanName);
try {
trigger.setCronExpression(quartzJob.getCronExpression());
} catch (ParseException e) {
e.printStackTrace();
}
sched.scheduleJob(quartzJob, trigger);
}
} catch (SchedulerException e) {
e.printStackTrace();
}
最后还是贴一下具体的用法吧
这里我没有引入其他的bean,不过显然上面的SpringBeanJobFactory已经实现了这个功能了。
<bean id="quartzBeanTest" class="com.crunii.emall.task.service.quartz.QuartzBeanTest"> <property name="cronExpression" value="0/10 * * * * ? "></property> </bean>
package com.crunii.emall.task.service.quartz;
import com.crunii.emall.quartz.QuartzJob;
public class QuartzBeanTest extends QuartzJob{
@Override
public void run() {
log.info("test");
}
}
这里没有详细的考虑过线程的问题,欢迎各个大神拍砖