【59】Quartz+Spring框架详解
什么是Quartz
Quartz是一个作业调度系统(a job scheduling system),Quartz不但可以集成到其他的软件系统中,而且也可以独立运行的;在本文中“job scheduler”的意思是:一个负责在约定的时间到达时执行(或通知)其他软件控件的方法。
Quartz是非常灵活的,为了实现我们的需求Quartz包含了许多可以独立或被集成使用的典型范例,同时使我们编写项目中的代码也觉得很简单自然(natural)。
Quartz是很轻量级的,只需要简单的安装或配置就可以在项目中使用;如果只是相对简单的使用实际上可以直接地使用(out-of-the-box)。
Quartz具有容错性,能够持久化所调度的作业,即在系统重启后也可以继续执行原先未完成的作业。
虽然Quartz对给定的计划可以简单地运行一些系统的处理是很适用的,但只有当我们学习如何使用Quartz去驱动我们的应用业务处理流程时,才能真正的认识到Quartz的全部潜能。
什么是Quartz-从软件控件的角度
Quartz是以.jar文件的形式发布的,Quartz java库中包含了Quartz所有的核心功能,该功能的主要接口(API)是Scheduler接口,Scheduler接口提供了一些简单的操作,如:schedulering/unscheduling jobs,starting/stopping/pausing the scheduler。
如果我们要调度自定义的作业,该作业必须实现Job接口,Job接口包含了一个必须实现的方法:execute(…);如果我们需要实现当到达被调度的时间能够得到通知,我们必须实现TriggerListener或JobListener接口。
Quartz主要程序不但能够作为独立的应用(带有RMI 接口)运行,也可以作为J2EE组件资源在J2EE应用服务器运行。
使用Quartz
使用Scheduler前必须实例化Scheduler,需要由SchedulerFactory类来创建Scheduler,Factory的实例可以通过在JNDI存储中的Factory的序列化的方式来获取,实例化Factory后直接使用该实例也是很容易的,如下面的例子。
Scheduler被实例化后就可以启动、暂停或关闭,但是Scheduler一旦关闭就不能再次启动该Scheduler,只有必须再次实例化后才可以;Trigger只有与之对应的Scheduler启动后才能触发与之相关的Job,否则一直处于暂停状态。
在Java领域中,定时任务的开源工具也非常多,小到一个Timer类,大到Quartz框架。总体来说,还是Quartz,功能强大而且使用方便。
Spring整合Quartz实现定时任务步骤
创建任务(Job)、配置JobDetail、配置触发器(Trigger)、配置SchedulerFactoryBean
前期准备:
Maven创建一个web项目并引入Spring和quartz的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>3.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
创建Spring配置文件
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
一、创建任务(Job)
Spring+Quartz实现Job有两种方式:一种是继承org.springframework.scheduling.quartz.QuartzJobBean类来实现Job任务,并实现里面的抽象方法executeInternal;另一种是不继承任何类,创建普通的Java类,然后自己指定任务的执行方法(个人感觉此种方式较好,实现起来方便而且大大降低了系统的业务的耦合性)。
我们先来看一下继承QuartzJobBean类的这种形式,创建一个任务类ExampleJob,具体代码如下:
package com.mhy.quartz;
/**
* 继承QuartzJobBean形式的定时任务
*/
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
/**
* @author mhy2011@163.com
* @date 2013年12月21日
*/
public class ExampleJob extends QuartzJobBean {
private int timeout;
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "执行ExampleJob的定时任务");
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
}
为了演示方便,executeInternal方法中没有写复杂的业务逻辑,只简单的输出一句话,真正的生产环境中在该方法中实现你所需要的业务逻辑即可。
二、在Spring配置文件中配置JobDetail
<bean name="exampleJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.mhy.quartz.ExampleJob" />
<property name="jobDataAsMap">
<map>
<entry key="timeout" value="5" />
</map>
</property>
</bean>
三、配置触发器(Trigger)
Spring提供了两种触发器,如下:
1、org.springframework.scheduling.quartz.SimpleTriggerFactoryBean(此种方式是很隔多长时间进行触发一次,比如每隔24小时触发一次)
2、org.springframework.scheduling.quartz.CronTriggerFactoryBean(此种方式是在指定的时间进行触发,比如只在周一进行触发。不过根据配置也很方便的实现类似SimpleTriggerFactoryBean形式的定时任务)
Spring所提供的这两种触发器方式和前面提到的任务创建方式均可以相互之间混用,很灵活。
这里我们先使用SimpleTriggerFactoryBean这个trigger来配置
<bean id="exampleJobTrigger"
class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="exampleJobDetail" />
<!-- 延迟触发时间,延迟10秒进行触发 -->
<property name="startDelay" value="10000" />
<!-- 重复触发的时间间隔,5秒 -->
<property name="repeatInterval" value="5000" />
</bean>
四、配置SchedulerFactoryBean
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="exampleJobTrigger" />
</list>
</property>
</bean>
计划任务 算法 表达 cron (Quartz cron 语法)
Quartz cron 表达式的格式十分类似于 UNIX cron 格式,但还是有少许明显的区别。区别之一就是 Quartz 的格式向下支持到秒级别的计划,而 UNIX cron 计划仅支持至分钟级。许多我们的触发计划要基于秒级递增的(例如,每45秒),因此这是一个非常好的差异。
Quartz 提供七个域。表 列出了 Quartz cron 表达式支持的七个域。
理解特殊字符
* 星号
使用星号(*) 指示着你想在这个域上包含所有合法的值。例如,在月份域上使用星号意味着每个月都会触发这个 trigger。
表达式样例:
0 * 17 * * ?
意义:每天从下午5点到下午5:59中的每分钟激发一次 trigger。它停在下午 5:59 是因为值 17 在小时域上,在下午 6 点时,小时变为 18 了,也就不再理会这个 trigger,直到下一天的下午5点。
在你希望 trigger 在该域的所有有效值上被激发时使用 * 字符。
? 问号
? 号只能用在日和周域上,但是不能在这两个域上同时使用。你可以认为? 字符是 “我并不关心在该域上是什么值。” 这不同于星号,星号是指示着该域上的每一个值。? 是说不为该域指定值。
不能同时这两个域上指定值的理由是难以解释甚至是难以理解的。基本上,假定同时指定值的话,意义就会变得含混不清了:考虑一下,如果一个表达式在日域上有值11,同时在周域上指定了WED。那么是要 trigger 仅在每个月的11号,且正好又是星期三那天被激发?还是在每个星期三的11号被激发呢?要去除这种不明确性的办法就是不能同时在这两个域上指定值。
只要记住,假如你为这两域的其中一个指定了值,那就必须在另一个字值上放一个 ?。
表达式样例:
0 10,44 14 ? 3 WEB
意义:在三月中的每个星期三的下午 2:10 和 下午 2:44 被触发。
, 逗号
逗号 (,) 是用来在给某个域上指定一个值列表的。例如,使用值 0,15,30,45 在秒域上意味着每15秒触发一个 trigger。
表达式样例:
0 0,15,30,45 * * * ?
意义:每刻钟触发一次 trigger。
/ 斜杠
斜杠 (/) 是用于时间表的递增的。我们刚刚用了逗号来表示每15分钟的递增,但是我们也能写成这样0/15。
表达式样例:
0/15 0/30 * * * ?
意义:在整点和半点时每15秒触发 trigger。
- 中划线
中划线 (-) 用于指定一个范围。例如,在小时域上的 3-8 意味着 “3,4,5,6,7 和 8 点。” 域的值不允许回卷,所以像 50-10 这样的值是不允许的。
表达式样例:
0 45 3-8 ? * *
意义:在上午的3点至上午的8点的45分时触发 trigger。
L 字母
L 说明了某域上允许的最后一个值。它仅被日和周域支持。当用在日域上,表示的是在月域上指定的月份的最后一天。例如,当月域上指定了JAN 时,在日域上的L 会促使 trigger 在1月31号被触发。假如月域上是SEP,那么 L 会预示着在9月30号触发。换句话说,就是不管指定了哪个月,都是在相应月份的时最后一天触发 trigger。
表达式 0 0 8 L * ? 意义是在每个月最后一天的上午 8:00 触发 trigger。在月域上的 * 说明是 “每个月”。
当 L 字母用于周域上,指示着周的最后一天,就是星期六 (或者数字7)。所以如果你需要在每个月的最后一个星期六下午的 11:59 触发 trigger,你可以用这样的表达式0 59 23 ? * L。
当使用于周域上,你可以用一个数字与 L 连起来表示月份的最后一个星期 X。例如,表达式 0 0 12 ? * 2L 说的是在每个月的最后一个星期一触发 trigger。
不要让范围和列表值与 L 连用
虽然你能用星期数(1-7)与 L 连用,但是不允许你用一个范围值和列表值与 L 连用。这会产生不可预知的结果。
W 字母
W 字符代表着平日 (Mon-Fri),并且仅能用于日域中。它用来指定离指定日的最近的一个平日。大部分的商业处理都是基于工作周的,所以 W 字符可能是非常重要的。例如,日域中的15W 意味着 “离该月15号的最近一个平日。” 假如15号是星期六,那么 trigger 会在14号(星期五)触发,因为星期四比星期一(这个例子中是17号)离15号更近。(译者Unmi注:不会在17号触发的,如果是15W,可能会是在14号(15号是星期六)或者15号(15号是星期天)触发,也就是只能出现在邻近的一天,如果15号当天为平日直接就会当日执行)。W 只能用在指定的日域为单天,不能是范围或列表值。
井号
字符仅能用于周域中。它用于指定月份中的第几周的哪一天。例如,如果你指定周域的值为6#3,它意思是某月的第三个周五 (6=星期五,#3意味着月份中的第三周)。另一个例子2#1 意思是某月的第一个星期一 (2=星期一,#1意味着月份中的第一周)。注意,假如你指定#5,然而月份中没有第 5 周,那么该月不会触发。
此处的 Cron 表达式 cookbook 旨在为常用的执行需求提供方案。尽管不可能列举出所有的表达式,但下面的应该为满足你的业务需求提供了足够的例子。
我的微信二维码如下,欢迎交流讨论
欢迎关注《IT面试题汇总》微信订阅号。每天推送经典面试题和面试心得技巧,都是干货!
微信订阅号二维码如下:
参考:
http://blog.csdn.net/liuyukuan/article/details/6709554
http://blog.csdn.net/u010397369/article/details/17465649#
http://wwwcomy.iteye.com/blog/1741596
【59】Quartz+Spring框架详解的更多相关文章
- Spring框架详解介绍-基本使用方法
1.Spring框架-控制反转(IOC) 2.Spring框架-面向切面编程(AOP) 3.Spring 内置的JdbcTemplate(Spring-JDBC) Spring框架-控制反转(IOC) ...
- Spring 框架 详解 (一)
Spring是分层的JavaSE/EE full-stack(一站式) 轻量级开源框架 * 分层: * SUN提供的EE的三层结构:web层.业务层.数据访问层(持久层,集成层) * Struts2是 ...
- spring框架详解: IOC装配Bean
1 Spring框架Bean实例化的方式: 提供了三种方式实例化Bean. 构造方法实例化:(默认无参数) 静态工厂实例化: 实例工厂实例化: 无参数构造方法的实例化: <!-- 默认情况下使用 ...
- Spring 框架 详解 (四)------IOC装配Bean(注解方式)
Spring的注解装配Bean Spring2.5 引入使用注解去定义Bean @Component 描述Spring框架中Bean Spring的框架中提供了与@Component注解等效的三个注 ...
- Spring 框架 详解 (三)-----IOC装配Bean
IOC装配Bean: 1.1.1 Spring框架Bean实例化的方式: 提供了三种方式实例化Bean. * 构造方法实例化:(默认无参数) * 静态工厂实例化: * 实例工厂实例化: 无参数构造方法 ...
- Spring 框架 详解 (二)
Spring的入门的程序: 1.1.1 下载Spring的开发包: spring-framework-3.2.0.RELEASE-dist.zip ---Spring开发包 * docs :sprin ...
- spring框架详解
把之前分享的spring框架整理一份放在这里. 整体架构: Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架 框架图(选自:http://docs.spring.io/spr ...
- Quartz.NET作业调度框架详解
Quartz.NET作业调度框架详解 http://www.cnblogs.com/lmule/archive/2010/08/28/1811042.html
- Spring配置文件详解 – applicationContext.xml文件路径
Spring配置文件详解 – applicationContext.xml文件路径 Java编程 spring的配置文件applicationContext.xml的默 ...
随机推荐
- Hadoop的RPC通信原理
RPC调用: RPC(remote procedure call)远程过程调用: 不同java进程间的对象方法的调用. 一方称作服务端(server),一方称为客户端(client): server端 ...
- ROS(indigo) 语音工具 科大讯飞 百度 pocketsphinx julius rospeex 16.11.22更新 ROS中文语音
ROS语音工具汇总,目前先给出链接,只用过一些简单的命令. 中文语音: 参考链接:使用科大讯飞库 1 http://www.ncnynl.com/archives/201611/1069.html 2 ...
- Appium webdriver的capabilities配置
Capabilities是由客户端发送给Appium服务器端的用来告诉服务器去启动哪种我们想要的会话的一套键值对集合.当中也有一些键值对是用来在自动化的过程中修改服务器端的行为方式. 必填的项目: d ...
- 用scheme重写Python的三大函数map reduce 和filter
重写过程中,发现这种做法能加深对递归的理解,而且reduce还体现了函数式编程是如何通过参数传递来实现命令式编程中的状态改变的. (define (imap f x . y) (if (null? y ...
- 反射模拟DbUtils实现ResultSet转成Bean实例
前几天接触到了apache的一个小框架DbUtils,真的被其优雅的设计所震撼到了,尤其是其中的 MyBean mybean = QueryRunner.query(sqlConnection,sql ...
- linuxsvn源代码版本库建立
linuxsvn源代码版本库建立 下面就要建立代码的版本库做描述: 1. 安装svn版本服务器端 yum install subversion 从镜像下载安装svn服务器端,我们服务器已经安装 ...
- 两个activity或者activity和fragment传值
使用Fragment的时候可能需要在两个Fragment之间进行参数的传递,开始想着可以使用SharedPreferences进行处理,想想这些简单的参数没有必要使用这么麻烦的方式去实现,翻了一下Fr ...
- 没想到你是这样的UDP
UDP是国际标准化组织为互联网设定的标准中的传输层中的一个协议.TCP/IP协议簇是一个很庞大的家族,但是今天我们就来看一看这个面向无连接的传输层在Java中是怎样通过编程实现的. 原理性知识 在Ja ...
- 最简单的基于librtmp的示例:发布H.264(H.264通过RTMP发布)
===================================================== 最简单的基于libRTMP的示例系列文章列表: 最简单的基于librtmp的示例:接收(RT ...
- Sybase - tempdb
前沿:换了新公司,公司使用的Sybase数据库.现在开始学习Sybase数据库了.希望未来的几个月能对Sybase由浅入深的了解和研究. Tempdb的作用 sybase server端内部使用 排序 ...