Spring+Quartz的整合有很多例子,此处不提整合;
若配置固定的job,常常使用MethodInvokingJobDetailFactoryBean,也不错, 可问题的根源在于 这个类没实现 Serializable接口, 导致了将job信息存入数据库中时,它不工作了,
这是诱因之一. 以下是文章的主要内容.
前提及目标
1.job信息存入数据库
2.可给项目添加固定的job(写在配置文件里的),也可以添加动态的job(如定时发送邮件,由JAVA代码添加job)
3.要能在实际项目中有使用价值
配置步骤开始(说白了,写程序就是写配置文件)
1. 配置DataSource, SchedulerFactoryBean
使用MYSQL
<!--dataSource--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
<!--schedulerFactory--> <bean id="schedulerFactory" lazy-init="true" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="autoStartup" value="${scheduler.auto.startup}"/> <property name="dataSource" ref="dataSource"/> <property name="triggers"> <list> <ref bean="testFixedTriggerBean"/> </list> </property> </bean>
MYSQL数据库的SQL文件见项目others目录, 也可去Quartz的网站下载http://www.quartz-scheduler.org/
2.配置固定的Job
由于当把job信息存入数据库时需要用到序列化(Serializable),不能使用MethodInvokingJobDetailFactoryBean对象了.
建议使用CronTriggerFactoryBean, 配置如下
<bean id="<span style="font-family: Arial, Helvetica, sans-serif;">testFixedTriggerBean</span><span style="font-family: Arial, Helvetica, sans-serif;">"</span> class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="testFixedJobDetailBean"/> <property name="cronExpression" value="${test.fixed.cron.expression}"/> </bean>${test.fixed.cron.expression}是对应的 cron expression,如 0/5 * * * * ?
配置对应的JobDetailFactoryBean
<bean id="testFixedJobDetailBean" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="com.andaily.service.scheduler.TestFixedJobDetailBean"/> <property name="durability" value="true"/> </bean>
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 大家一起讨论,学习.