一、定时调度
定时调度指的是在一定时间条件下,系统自动完成特定功能的运行机制。传统的定时调度分为两种:
对于定时调度而言,所依赖的便是计算机系统底层的时钟发生器。在 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/taskhttp://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;@Componentpublic 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;@Componentpublic 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 $$$
       复制代码
 
评论