在项目中有一个需求,需要灵活配置调度任务时间,并能自由启动或停止调度。
有关调度的实现我就第一就想到了Quartz这个开源调度组件,因为很多项目使用过,Spring结合Quartz静态配置调度任务时间,非常easy。比如:每天凌晨几点定时运行一个程序,这只要在工程中的spring配置文件中配置好spring整合quartz的几个属性就好。
- <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
- <property name="targetObject" ref="simpleService" />
- <property name="targetMethod" value="test" />
- </bean>
- <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
- <property name="jobDetail" ref="jobDetail" />
- <property name="cronExpression" value="0 0/50 * ? * * *" />
- </bean>
- <bean id="schedulerTrigger" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
- <property name="triggers">
- <list>
- <ref bean="cronTrigger"/>
- </list>
- </property>
- </bean>
这种配置就是对quartz的一种简单的使用了,调度任务会在spring启动的时候加载到内存中,按照cronTrigger中定义的 cronExpression定义的时间按时触发调度任务。但是这是quartz使用“内存”方式的一种配置,也比较常见,当然对于不使用spring的项目,也可以单独整合quartz。方法也比较简单,可以从quartz的doc中找到配置方式,或者看一下《Quartz Job Scheduling Framework 》。
但是对于想持久化调度任务的状态,并且灵活调整调度时间的方式来说,上面的内存方式就不能满足要求了,正如本文开始我遇到的情况,需要采用数据库方式集成 Quartz,这部分集成其实在《Quartz Job Scheduling Framework 》中也有较为详细的介绍,当然doc文档中也有,但是缺乏和spring集成的实例。
一、需要构建Quartz数据库表,建表脚本在Quartz发行包的docs\dbTables目录,里面有各种数据库建表脚本,例子中采用的Quartz 2.2.1版本,使用H2内存数据库,执行了Quartz发行包的docs\dbTables\tables_h2.sql建表。
二、建立Maven project,完成后目录如下
![](https://i-blog.csdnimg.cn/blog_migrate/807a20fbf3a9891dfe75f1238fc0a6b5.png)
三、配置applicationContext.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
">
<context:component-scan base-package="com.sundoctor" />
<!-- 使用H2内存数据库并创建quartz数据库表 -->
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="classpath:db/tables_h2.sql"/>
</jdbc:embedded-database>
</beans>
这里只是配置了数据库连接dataSource,为了简单方便,我使用H2内存数据库,执行tables_h2.sql创建quartz数据库表,还没有涉及到quartz有关配置,下面且听我慢慢道来。
四、实现动态定时任务
什么是动态定时任务:是由客户制定生成的,服务端只知道该去执行什么任务,但任务的定时是不确定的(是由客户制定)。
这样总不能修改配置文件每定制个定时任务就增加一个trigger吧,即便允许客户修改配置文件,但总需要重新启动web服务啊,研究了下Quartz在Spring中的动态定时,发现
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="0 0/50 * ? * * *" />
</bean>
中cronExpression是关键,如果可以动态设置cronExpression的值,就可以顺利解决问题了。这样我们就不能直接使用org.springframework.scheduling.quartz.CronTriggerFactoryBean,需要自己实现一个动态调度服务类,在其中构建CronTrigger或SimpleTrigger,动态配置时间。
动态调度服务接口:
- package com.sundoctor.quartz.service;
-
- import java.util.Date;
-
- import org.quartz.CronExpression;
-
- public interface SchedulerService {
-
-
-
-
-
-
- void schedule(String cronExpression);
-
-
-
-
-
-
-
-
-
- void schedule(String name, String cronExpression);
-
-
-
-
-
-
-
-
-
-
-
- void schedule(String name, String group, String cronExpression);
-
-
-
-
-
-
-
- void schedule(CronExpression cronExpression);
-
-
-
-
-
-
-
-
-
- void schedule(String name, CronExpression cronExpression);
-
-
-
-
-
-
-
-
-
-
-
- void schedule(String name, String group, CronExpression cronExpression);
-
-
-
-
-
-
-
- void schedule(Date startTime);
-
- void schedule(Date startTime, String group);
-
-
-
-
-
-
-
-
-
- void schedule(String name, Date startTime);
-
- void schedule(String name, Date startTime, String group);
-
-
-
-
-
-
-
-
-
- void schedule(Date startTime, Date endTime);
-
- void schedule(Date startTime, Date endTime, String group);
-
-
-
-
-
-
-
-
-
-
-
- void schedule(String name, Date startTime, Date endTime);
-
- void schedule(String name, Date startTime, Date endTime, String group);
-
-
-
-
-
-
-
-
-
- void schedule(Date startTime, int repeatCount);
-
-
-
-
-
-
-
-
-
-
-
- void schedule(Date startTime, Date endTime, int repeatCount);
-
- void schedule(Date startTime, Date endTime, int repeatCount, String group);
-
-
-
-
-
-
-
-
-
-
-
-
-
- void schedule(String name, Date startTime, Date endTime, int repeatCount);
-
- void schedule(String name, Date startTime, Date endTime, int repeatCount, String group);
-
-
-
-
-
-
-
-
-
-
-
-
- void schedule(Date startTime, int repeatCount, long repeatInterval);
-
-
-
-
-
-
-
-
-
-
-
-
-
- void schedule(Date startTime, Date endTime, int repeatCount, long repeatInterval);
-
- void schedule(Date startTime, Date endTime, int repeatCount, long repeatInterval, String group);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- void schedule(String name, Date startTime, Date endTime, int repeatCount, long repeatInterval);
-
- void schedule(String name, Date startTime, Date endTime, int repeatCount, long repeatInterval, String group);
-
-
-
-
-
-
-
- void pauseTrigger(String triggerName);
-
-
-
-
-
-
-
-
-
- void pauseTrigger(String triggerName, String group);
-
-
-
-
-
-
-
- void resumeTrigger(String triggerName);
-
-
-
-
-
-
-
-
-
- void resumeTrigger(String triggerName, String group);
-
-
-
-
-
-
-
-
- boolean removeTrigdger(String triggerName);
-
-
-
-
-
-
-
-
-
-
- boolean removeTrigdger(String triggerName, String group);
- }
动态调度服务实现类:
- package com.sundoctor.quartz.service;
-
- import java.text.ParseException;
- import java.util.Date;
- import java.util.UUID;
-
- import org.quartz.CronExpression;
- import org.quartz.JobDetail;
- import org.quartz.Scheduler;
- import org.quartz.SchedulerException;
- import org.quartz.TriggerKey;
- import org.quartz.impl.triggers.CronTriggerImpl;
- import org.quartz.impl.triggers.SimpleTriggerImpl;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- @Service("schedulerService")
- public class SchedulerServiceImpl implements SchedulerService {
-
- private static final String NULLSTRING = null;
- private static final Date NULLDATE = null;
-
- @Autowired
- private Scheduler scheduler;
- @Autowired
- private JobDetail jobDetail;
-
- @Override
- public void schedule(String cronExpression) {
- schedule(NULLSTRING, cronExpression);
- }
-
- @Override
- public void schedule(String name, String cronExpression) {
- schedule(name, NULLSTRING, cronExpression);
- }
-
- @Override
- public void schedule(String name, String group, String cronExpression) {
- try {
- schedule(name, group, new CronExpression(cronExpression));
- } catch (ParseException e) {
- throw new IllegalArgumentException(e);
- }
- }
-
- @Override
- public void schedule(CronExpression cronExpression) {
- schedule(NULLSTRING, cronExpression);
- }
-
- @Override
- public void schedule(String name, CronExpression cronExpression) {
- schedule(name, NULLSTRING, cronExpression);
- }
-
- @Override
- public void schedule(String name, String group, CronExpression cronExpression) {
-
- if (isValidExpression(cronExpression)) {
-
- if (name == null || name.trim().equals("")) {
- name = UUID.randomUUID().toString();
- }
-
- CronTriggerImpl trigger = new CronTriggerImpl();
- trigger.setCronExpression(cronExpression);
-
- TriggerKey triggerKey = new TriggerKey(name, group);
-
- trigger.setJobName(jobDetail.getKey().getName());
- trigger.setKey(triggerKey);
-
- try {
- scheduler.addJob(jobDetail, true);
- if (scheduler.checkExists(triggerKey)) {
- scheduler.rescheduleJob(triggerKey, trigger);
- } else {
- scheduler.scheduleJob(trigger);
- }
- } catch (SchedulerException e) {
- throw new IllegalArgumentException(e);
- }
- }
- }
-
- @Override
- public void schedule(Date startTime) {
- schedule(startTime, NULLDATE);
- }
-
- @Override
- public void schedule(Date startTime, String group) {
- schedule(startTime, NULLDATE, group);
- }
-
- @Override
- public void schedule(String name, Date startTime) {
- schedule(name, startTime, NULLDATE);
- }
-
- @Override
- public void schedule(String name, Date startTime, String group) {
- schedule(name, startTime, NULLDATE, group);
- }
-
- @Override
- public void schedule(Date startTime, Date endTime) {
- schedule(startTime, endTime, 0);
- }
-
- @Override
- public void schedule(Date startTime, Date endTime, String group) {
- schedule(startTime, endTime, 0, group);
- }
-
- @Override
- public void schedule(String name, Date startTime, Date endTime) {
- schedule(name, startTime, endTime, 0);
- }
-
- @Override
- public void schedule(String name, Date startTime, Date endTime, String group) {
- schedule(name, startTime, endTime, 0, group);
- }
-
- @Override
- public void schedule(Date startTime, int repeatCount) {
- schedule(null, startTime, NULLDATE, 0);
- }
-
- @Override
- public void schedule(Date startTime, Date endTime, int repeatCount) {
- schedule(null, startTime, endTime, 0);
- }
-
- @Override
- public void schedule(Date startTime, Date endTime, int repeatCount, String group) {
- schedule(null, startTime, endTime, 0, group);
- }
-
- @Override
- public void schedule(String name, Date startTime, Date endTime, int repeatCount) {
- schedule(name, startTime, endTime, 0, 0L);
- }
-
- @Override
- public void schedule(String name, Date startTime, Date endTime, int repeatCount, String group) {
- schedule(name, startTime, endTime, 0, 0L, group);
- }
-
- @Override
- public void schedule(Date startTime, int repeatCount, long repeatInterval) {
- schedule(null, startTime, NULLDATE, repeatCount, repeatInterval);
- }
-
- @Override
- public void schedule(Date startTime, Date endTime, int repeatCount, long repeatInterval) {
- schedule(null, startTime, endTime, repeatCount, repeatInterval);
- }
-
- @Override
- public void schedule(Date startTime, Date endTime, int repeatCount, long repeatInterval, String group) {
- schedule(null, startTime, endTime, repeatCount, repeatInterval, group);
- }
-
- @Override
- public void schedule(String name, Date startTime, Date endTime, int repeatCount, long repeatInterval) {
- schedule(name, startTime, endTime, repeatCount, repeatInterval, NULLSTRING);
- }
-
- @Override
- public void schedule(String name, Date startTime, Date endTime, int repeatCount, long repeatInterval, String group) {
-
- if (this.isValidExpression(startTime)) {
-
- if (name == null || name.trim().equals("")) {
- name = UUID.randomUUID().toString();
- }
-
- TriggerKey triggerKey = new TriggerKey(name, group);
-
- SimpleTriggerImpl trigger = new SimpleTriggerImpl();
- trigger.setKey(triggerKey);
- trigger.setJobName(jobDetail.getKey().getName());
-
- trigger.setStartTime(startTime);
- trigger.setEndTime(endTime);
- trigger.setRepeatCount(repeatCount);
- trigger.setRepeatInterval(repeatInterval);
-
- try {
- scheduler.addJob(jobDetail, true);
- if (scheduler.checkExists(triggerKey)) {
- scheduler.rescheduleJob(triggerKey, trigger);
- } else {
- scheduler.scheduleJob(trigger);
- }
- } catch (SchedulerException e) {
- throw new IllegalArgumentException(e);
- }
- }
- }
-
- @Override
- public void pauseTrigger(String triggerName) {
- pauseTrigger(triggerName, NULLSTRING);
- }
-
- @Override
- public void pauseTrigger(String triggerName, String group) {
- try {
- scheduler.pauseTrigger(new TriggerKey(triggerName, group));
- } catch (SchedulerException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public void resumeTrigger(String triggerName) {
- resumeTrigger(triggerName, NULLSTRING);
- }
-
- @Override
- public void resumeTrigger(String triggerName, String group) {
- try {
- scheduler.resumeTrigger(new TriggerKey(triggerName, group));
- } catch (SchedulerException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public boolean removeTrigdger(String triggerName) {
- return removeTrigdger(triggerName, NULLSTRING);
- }
-
- @Override
- public boolean removeTrigdger(String triggerName, String group) {
- TriggerKey triggerKey = new TriggerKey(triggerName, group);
- try {
- scheduler.pauseTrigger(triggerKey);
- return scheduler.unscheduleJob(triggerKey);
- } catch (SchedulerException e) {
- throw new RuntimeException(e);
- }
- }
-
- private boolean isValidExpression(final CronExpression cronExpression) {
-
- CronTriggerImpl trigger = new CronTriggerImpl();
- trigger.setCronExpression(cronExpression);
-
- Date date = trigger.computeFirstFireTime(null);
-
- return date != null && date.after(new Date());
- }
-
- private boolean isValidExpression(final Date startTime) {
-
- SimpleTriggerImpl trigger = new SimpleTriggerImpl();
- trigger.setStartTime(startTime);
-
- Date date = trigger.computeFirstFireTime(null);
-
- return date != null && date.after(new Date());
- }
- }
SchedulerService 只有一个多态方法schedule,SchedulerServiceImpl实现SchedulerService接口,注入org.quartz.Schedulert和org.quartz.JobDetail,schedule方法可以动态配置org.quartz.CronExpression或org.quartz.SimpleTrigger调度时间。
五、实现自己的org.quartz.JobDetail
- package com.sundoctor.example.service;
-
- import org.quartz.JobExecutionContext;
- import org.quartz.JobExecutionException;
- import org.quartz.SchedulerException;
- import org.quartz.Trigger;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.context.ApplicationContext;
- import org.springframework.scheduling.quartz.QuartzJobBean;
-
- public class MyQuartzJobBean extends QuartzJobBean {
-
- private static final Logger logger = LoggerFactory.getLogger(MyQuartzJobBean.class);
-
- @Override
- protected void executeInternal(JobExecutionContext jobexecutioncontext) throws JobExecutionException {
- Trigger trigger = jobexecutioncontext.getTrigger();
- String triggerName = trigger.getKey().getName();
-
- SimpleService simpleService = getApplicationContext(jobexecutioncontext).getBean("simpleService",
- SimpleService.class);
- simpleService.testMethod(triggerName);
-
- }
-
- private ApplicationContext getApplicationContext(final JobExecutionContext jobexecutioncontext) {
- try {
- return (ApplicationContext) jobexecutioncontext.getScheduler().getContext().get("applicationContextKey");
- } catch (SchedulerException e) {
- logger.error("jobexecutioncontext.getScheduler().getContext() error!", e);
- throw new RuntimeException(e);
- }
- }
-
- }
MyQuartzJobBean继承
org.springframework.scheduling.quartz.QuartzJobBean
,SimpleService如下: