项目中要用到定时调度的功能,使用的是quartz,使用中遇到了一些问题,特地记下来。
一、quartz概述
quartz是一个任务定时调度的框架,支持定时的调用特定的方法,支持与Spring的整合。
二、配置步骤
quartz与Spring整合,需要一个配置文件来管理quartz中的类。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" >
<beans>
<!-- 实际的工作Bean -->
<bean id="workBean" class="WorkClass">
</bean>
<!-- jobBean用于设定启动时运用的Bean与方法 -->
<bean id="scheduledReportJobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject">
<ref bean="workBean" />
</property>
<property name="targetMethod">
<value>work</value>
</property>
</bean>
<!-- 每5秒启动一次 -->
<bean id="trigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<ref bean="scheduledReportJobDetail" />
</property>
<property name="cronExpression">
<value> 0/5 * * * * ?</value>
</property>
</bean>
<!-- 起动Bean -->
<bean id="scheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="trigger" />
</list>
</property>
</bean>
</beans>
配置文件中配置的几个bean,依次代表,实际工作的bean、封装后用于调度的bean、配置有时间间隔的trigger触发器bean和用于启动任务的主类工厂bean。
这样,创建自己的调度就变得非常简单,我们只需要完成自己的工作方法,在xml中配置好,并制定一个时间间隔,然后启动主类就可以了。
public class WorkClass {
public void work() {
System.out.println("hello world");
}
public static void main(String[] args) {
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("scheduleTask.xml"));
System.out.println(factory.getBean("scheduler"));
}
}
当然,除了和Spring整合之外,Quartz也可以单独用。
同Spring中的工作类不一样,不用Spring的工作类需要继承Job接口。
public class TestWithoutSpring implements Job {
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println("work");
}
}
而触发器和调度者的信息则需要在调用处指定。
public void testWork() {
JobDetail jobDetail = new JobDetail();
jobDetail.setJobClass(TestWithoutSpring.class);
jobDetail.setName("asdf");
CronTrigger trigger = new CronTrigger();
trigger.setName("test");
String expression = "2-3 0/1 * * * ?";
Scheduler scheduler = null;
try {
trigger.setCronExpression(expression);
SchedulerFactory sf = new StdSchedulerFactory();
scheduler = sf.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
} catch (ParseException e) {
e.printStackTrace();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
启动以后就和使用Spring管理是一样的效果了。
三、时间间隔配置
在触发器bean中有一个重要的属性叫cronExpression,这个是代表调度频率,也就是时间间隔的,这里有特殊的格式。
quartz中的时间格式有7个元素组成,分别为秒、分、时、日、月、周几、年(可选),反应在配置中就是7(6)个由空格分隔的字符。
秒 0-59 , - * /
分 0-59 , - * /
小时 0-23 , - * /
日 1-31 , - * ? / L W C
月 1-12 or JAN-DEC , - * /
周几 1-7 or SUN-SAT , - * ? / L C #
年 (可选字段) empty, 1970-2099 , - * /
这里常规的配置就是直接输入数字,但是quartz允许我们使用更加泛化的配置,所以提供了一些通配符。
* 表示用于替代任意数字
/表示增加幅度, 5/10表示从5开始,每10执行一次
-表示一个区间范围
? 在日和周几中出现,这里是用来明确界定,以免产生歧义
如cronExpression配置为 0/5 * * * * * 时会报错:Support for specifying both a day-of-week AND a day-of-month parameter is not implemented, 这就是日和周几产生了冲突, 改为 0/5 * * * * ? 就可以了,表示每5秒执行一次任务。
P.S quartz一开始是没有和Spring做整合的而且随着版本的迭代个中的类有一些变化, 这里就要注意quartz和Spring的版本匹配问题。
四、深入
Spring管理的quartz类变得很抽象,实际上,quar中有几个核心的类。
Job接口,实现了Job接口的类是任务中的实际工作类,对应于workBean
JobDetail类,包装Job,对应于scheduledReportJobDetail,利用反射在实际的调度中执行特定的方法
Trigger类,这是触发器类,用于封装调度频率时间间隔的逻辑,对应于trigger
Scheduler类,这是调度类,是封装了整个调度逻辑的最高层抽象,它持有代表任务的JobDetail类和代表调度频率的Trigger类,是调度逻辑的执行者和管理者。
了解了主要的类,要执行管理就很简单了,比如要控制调度的暂停或者关闭,只要通过Spring拿到对应的Scheduler类,调用其方法就可以了