一、定时调度
定时调度指的是在一定时间条件下,系统自动完成特定功能的运行机制。传统的定时调度分为两种:
对于定时调度而言,所依赖的便是计算机系统底层的时钟发生器。在 Java 技术中,提供了两个操作类:Timer、TimerTask,其中,TimerTask 用于定义定时调度所要执行的任务和功能,线程运行时调用。
范例:
new Timer().scheduleAtFixedRate(new TimerTask() {
private Logger logger = LoggerFactory.getLogger(TimerTask.class);
@Override
public void run() {
logger.info(new Date().toString());
}
}, new Date(), 2000);
复制代码
随着任务调度的复杂度提升,单纯地使用以上基础类进行编写已显得非常麻烦,因此,对于企业级项目而言,提供了以下两种方式实现任务调度。
Quartz:第三方的任务调度组件,需要进行配置;
Spring Task:spring 提供了任务调度模块,配置简单,可使用 annotation 实现。
二、Quartz
项目 pom.xml 文件配置 Quartz 组件。在实际开发中,Quartz 提供了两种实现方式:
直接继承父类实现;
配置方式实现任务调度,不需要继承父类。
建议不要使用直接继承父类的方式,这样会增加过多的硬编码,也同时增加了代码量,不利于维护。
一下介绍基于 spring 配置实现 Quartz 任务调度。
2.1、继承 QuartzJobBean 实现定时任务
定义任务处理类(org.springframework.scheduling.quartz.QuartzJobBean)
package org.fuys.own.test;
import java.util.Date;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;
/**
* test QuartzJobBean class to execute time task
* @author ys
*/
public class QuartzJob extends QuartzJobBean{
private Logger logger = LoggerFactory.getLogger(QuartzJob.class);
@Override
protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
logger.info(new Date().toString());
}
}
复制代码
配置任务调度类(org.springframework.scheduling.quartz.JobDetailFactoryBean),即任务的调度由工厂类实现
<bean id="jobFactory" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.fuys.own.test.QuartzJob"/>
<property name="jobDataMap">
<map>
<entry key="timeout" value="0"/>
</map>
</property>
</bean>
复制代码
配置任务调度的触发类(org.springframework.scheduling.quartz.SimpleTriggerFactoryBean),并配置触发时间
<bean id="jobTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="jobFactory"/>
<property name="startDelay" value="0"/>
<property name="repeatInterval" value="2000"/>
</bean>
复制代码
配置日程调度类(org.springframework.scheduling.quartz.SchedulerFactoryBean),即启动任务调度的触发
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<array>
<ref bean="jobTrigger"/>
</array>
</property>
</bean>
复制代码
配置好以上信息,定时程序交由 spring 容器自动处理。
2.2、使用 Cron 实现定时任务
Quartz 最大的好处在于使用 Cron 表达式进行定时任务的触发。
Cron 表达式的语法格式有两种:
具体的规则可以查看网络资料,无需记忆,具体问题时再查看即可。
对于程序而言,只需要将定时任务的触发类修改为支持 Cron 表达式的类即可,以下展示 Quartz 和 Cron 一起实现触发的比较。
<bean id="jobTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="jobFactory"/>
<property name="startDelay" value="1"/>
<property name="repeatInterval" value="7000"/>
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="jobFactory"/>
<property name="cronExpression" value="0/30 * * * * ?"/>
</bean>
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<array>
<ref bean="jobTrigger"/>
<ref bean="cronTrigger"/>
</array>
</property>
</bean>
复制代码
2.3、无需继承父类的定时任务实现
无需继承父类的定时任务实现,只需要变更任务调度类,将其修改为以下类:
org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean
复制代码
配置信息如下:
<bean id="methodInvokingJobFactory" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject">
<bean class="org.fuys.own.test.TimerDemo"/>
</property>
<property name="targetMethod" value="executeTimer"/>
</bean>
复制代码
三、Spring Task
基于第三方组件 Quartz 的定时任务实现,配置过于复杂,不利于简便开发,因此,spring 提供了 task 组件,为开发者快速简便的进行定时任务实现提供了工具。
Task 组件的实现有两种方式:
配置文件添加 task 规则:
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd"
复制代码
3.1、基于配置文件的实现
Java 类:
package org.fuys.own.test;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SpringTaskDemo {
private Logger logger = LoggerFactory.getLogger(SpringTaskDemo.class);
public void executeTask(){
logger.info("===>" + new Date().toString() + "<===");
}
}
复制代码
applicationContext.xml 文件配置:
<bean id="springTask" class="org.fuys.own.test.SpringTaskDemo"/>
<task:scheduled-tasks>
<!-- 间隔执行 -->
<task:scheduled ref="springTask" method="executeTask" fixed-delay="3000"/>
<!-- Cron表达式 -->
<task:scheduled ref="springTask" method="executeTask" cron="0/8 * * * * ?"/>
</task:scheduled-tasks>
复制代码
3.2、基于 Annotation 的实现
因为有了 Annotation 这一特点,spring 容器的支持,所以,不需要配置那么多复杂的继承结构。使用 Spring Task 的注解配置,需要在配置文件中添加 task 的注解驱动支持。
配置文件:
<task:annotation-driven/>
复制代码
Java 程序:
package org.fuys.own.test;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class SpringTaskAnnotationDemo {
private Logger logger = LoggerFactory.getLogger(SpringTaskAnnotationDemo.class);
// 定时调度
@Scheduled(cron="0/6 * * * * ?")
public void executeSpringTask(){
logger.info("$$$ " + new Date().toString() + " $$$");
}
// 间隔调度
@Scheduled(fixedRate=4000)
public void executeTaskAnnotation(){
logger.info(">>> " + new Date().toString() + " <<<");
}
}
复制代码
3.3、任务调度池
下面看下程序代码,分析其情况。
Java 代码:
package org.fuys.own.test;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class SpringTaskAnnotationDemo {
private Logger logger = LoggerFactory.getLogger(SpringTaskAnnotationDemo.class);
// 定时调度
@Scheduled(cron="0/6 * * * * ?")
public void executeSpringTask(){
logger.info("$$$ " + new Date().toString() + " $$$");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 间隔调度
@Scheduled(fixedRate=4000)
public void executeTaskAnnotation(){
logger.info(">>> " + new Date().toString() + " <<<");
}
}
复制代码
程序执行后的结果为:
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:48:58 CST 2018 <<<
INFO SpringTaskAnnotationDemo - $$$ Wed Jan 03 03:49:00 CST 2018 $$$
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:49:10 CST 2018 <<<
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:49:10 CST 2018 <<<
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:49:10 CST 2018 <<<
INFO SpringTaskAnnotationDemo - $$$ Wed Jan 03 03:49:12 CST 2018 $$$
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:49:22 CST 2018 <<<
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:49:22 CST 2018 <<<
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:49:22 CST 2018 <<<
INFO SpringTaskAnnotationDemo - $$$ Wed Jan 03 03:49:24 CST 2018 $$$
复制代码
由此得知,对于 Spring task 组件而言,定时任务是顺序调度执行,当上一个程序没有执行完时,剩下的程序只能进行挂起等待。因此,这对于实际应用而言,是非常不利的,故 Spring 提出了任务调度池的解决方案,可以并行执行多个程序。
添加以下配置即可实现任务调度池。
<task:scheduler id="schedulerPool" pool-size="10"/>
复制代码
将以上 Java 程序执行之后的结果为:
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:58:37 CST 2018 <<<
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:58:41 CST 2018 <<<
INFO SpringTaskAnnotationDemo - $$$ Wed Jan 03 03:58:42 CST 2018 $$$
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:58:45 CST 2018 <<<
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:58:49 CST 2018 <<<
INFO SpringTaskAnnotationDemo - >>> Wed Jan 03 03:58:53 CST 2018 <<<
INFO SpringTaskAnnotationDemo - $$$ Wed Jan 03 03:58:54 CST 2018 $$$
复制代码
评论