Spring+Quartz的整合有很多例子,此处不提整合;
若配置固定的job,常常使用MethodInvokingJobDetailFactoryBean,也不错, 可问题的根源在于 这个类没实现 Serializable接口, 导致了将job信息存入数据库中时,它不工作了,
这是诱因之一. 以下是文章的主要内容.
前提及目标
1.job信息存入数据库
2.可给项目添加固定的job(写在配置文件里的),也可以添加动态的job(如定时发送邮件,由JAVA代码添加job)
3.要能在实际项目中有使用价值
配置步骤开始(说白了,写程序就是写配置文件)
1. 配置DataSource, SchedulerFactoryBean
使用MYSQL
MYSQL数据库的SQL文件见项目others目录, 也可去Quartz的网站下载http://www.quartz-scheduler.org/
2.配置固定的Job
由于当把job信息存入数据库时需要用到序列化(Serializable),不能使用MethodInvokingJobDetailFactoryBean对象了.
建议使用CronTriggerFactoryBean, 配置如下
testFixedTriggerBean" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
${test.fixed.cron.expression}是对应的 cron expression,如 0/5 * * * * ?
配置对应的JobDetailFactoryBean
public class TestFixedJobDetailBean extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("I am working on " + new Date());
}
}
注意: 由于此处的代码没有 事务, 若业务方法需要事务,要自己去处理下.
3.配置动态的Job
所谓动态, 就是在程序运行中, 由使用的人手动去加入的Job, 常用的如 邮件定时发送, 任务提醒等
其实质就是把Job告诉 schedulerFactory, 让其去处理
为此我们添加以下对象
DynamicSchedulerFactory -- 管理动态Job的注册,删除等, 需要配置在xml配置文件中; 当然,肯定需要schedulerFactory对象的引用
DynamicJob -- 包含动态Job需要的所有信息,如 cron expression等.由程序需要时创建
public final class DynamicSchedulerFactory implements InitializingBean {
private static final Logger LOG = LoggerFactory.getLogger(DynamicSchedulerFactory.class);
private static Scheduler scheduler;
public DynamicSchedulerFactory() {
}
/**
* Register a job
*
* @param job DynamicJob
* @return True is register successful
* @throws SchedulerException
*/
public static boolean registerJob(DynamicJob job) throws SchedulerException {
final TriggerKey triggerKey = job.triggerKey();
if (scheduler.checkExists(triggerKey)) {
final Trigger trigger = scheduler.getTrigger(triggerKey);
throw new SchedulerException("Already exist trigger [" + trigger + "] by key [" + triggerKey + "] in Scheduler");
}
final CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.cronExpression());
final CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
.withSchedule(cronScheduleBuilder)
.build();
final JobDetail jobDetail = job.jobDetail();
final Date date = scheduler.scheduleJob(jobDetail, cronTrigger);
LOG.debug("Register DynamicJob {} on [{}]", job, date);
return true;
}
/**
* Remove exists job
*
* @param existJob A DynamicJob which exists in Scheduler
* @return True is remove successful
* @throws SchedulerException
*/
public static boolean removeJob(DynamicJob existJob) throws SchedulerException {
final TriggerKey triggerKey = existJob.triggerKey();
boolean result = false;
if (scheduler.checkExists(triggerKey)) {
result = scheduler.unscheduleJob(triggerKey);
}
LOG.debug("Remove DynamicJob {} result [{}]", existJob, result);
return result;
}
public void setScheduler(Scheduler scheduler) {
DynamicSchedulerFactory.scheduler = scheduler;
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(scheduler, "scheduler is null");
LOG.info("Initial DynamicSchedulerFactory successful, scheduler instance: {}", scheduler);
}
public class DynamicJob {
//Job class
private Class extends Job> target;
//cron expression
private String cronExpression;
private String jobGroup = Scheduler.DEFAULT_GROUP;
//Must unique
private String jobName;
private transient TriggerKey triggerKey;
private transient JobDetail jobDetail;
//default
public DynamicJob() {
}
public DynamicJob(String jobName) {
this.jobName = jobName;
}
public Class extends Job> target() {
return target;
}
public DynamicJob target(Class extends Job> target) {
this.target = target;
return this;
}
public DynamicJob cronExpression(String cronExpression) {
this.cronExpression = cronExpression;
return this;
}
public String jobGroup() {
return jobGroup;
}
public DynamicJob jobGroup(String jobGroup) {
this.jobGroup = jobGroup;
return this;
}
public String jobName() {
return jobName;
}
public DynamicJob jobName(String jobName) {
this.jobName = jobName;
return this;
}
public TriggerKey triggerKey() {
if (triggerKey == null) {
triggerKey = TriggerKey.triggerKey(this.jobName, this.jobGroup);
}
return triggerKey;
}
public JobDetail jobDetail() {
if (jobDetail == null) {
jobDetail = JobBuilder.newJob(target)
.withIdentity(this.jobName, this.jobGroup)
.build();
}
return jobDetail;
}
/*
* Transfer data to job
* In job use
* context.getMergedJobDataMap().get(key)
* */
public DynamicJob addJobData(String key, Object value) {
final JobDetail detail = jobDetail();
final JobDataMap jobDataMap = detail.getJobDataMap();
jobDataMap.put(key, value);
return this;
}
public String cronExpression() {
return this.cronExpression;
}
}
有了这两个基本的类, 实现动态的Job如下
1). 创建DynamicJob实例(jobName,cronExpression与target是必须的; target是Job类的一个实现)
2).调用DynamicSchedulerFactory的registerJob 注册一个Job或者 调用removeJob删除已添加的Job
一个示例如下:
DynamicJob dynamicJob = createDynamicJob();
dynamicJob.addJobData("mailGuid", mail.guid());//transfer parameter
try {
DynamicSchedulerFactory.registerJob(dynamicJob);
} catch (SchedulerException e) {
throw new IllegalStateException(e);
}
NOTE: 关于DynamicJob中的target.
这个字段是一个 Class类型, 要求实现是 一个Job的实现类.这不具体讲了吧.
到此, 完成;
我已将示例的代码放到GIT上, 地址: http://git.oschina.net/mkk/spring-dynamic-job 大家一起讨论,学习.