1 前言
本文档是描述Spring服务开发过程,用于开发人员培训和交流,有不足之处还望谅解和指出。
2 Spring定时服务
2.1 介绍
Spring定时服务包括 Spring自带定时服务和集成 Quartz定时服务框架两类。![](http://static.oschina.net/uploads/space/2013/0801/105156_Q5AP_1177247.jpg)
Spring定时服务依赖于jar包是Spring.jar,这个包不用多说了,是Spring Framework必备的一个包。
3 Spring自带定时服务
1、必须继承java.util.TimerTask的JaveBean。
2、注册到Spring框架提供的任务调度org.springframework.scheduling.timer.ScheduledTimerTask当中。
3.1 开发过程
3.1.1 继承TimerTask
package test.timerTask;
import java.util.TimerTask;
public class Task extends TimerTask {
public void run() {
// TODO Auto-generated method stub
System.out.println("测试TimerTask : Hello !!");
}
}
3.1.2 注册Spring任务调度
<bean id="sayHelloTask" class="test.springTimer.Task"></bean>
<bean id="scheduledTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="timerTask">
<ref bean="sayHelloTask"/>
</property>
<!-- 任务执行周期 2m 关于一些任务的参数请参考JDK doc文档和Spring相关文档-->
<property name="period">
<value>2000</value>
</property>
<!-- 延时1m 执行任务 -->
<property name="delay">
<value>1000</value>
</property>
</bean>
4 集成Quartz定时服务框架
Quartz优点是:
1、能嵌入到任何独立的应用中运行。
2、能在应用服务器或者Servlet 容器中实例化,并且能够参与XA事务。
3、能够以独立的方式运行(在它自己的Java 虚拟机中),可以通过RMI 使用Quartz。
4、可以被实例化为独立程序的集群(有负载均衡和容错能力)。
4.1 Quartz触发器
4.1.1 SimpleTrigger
如果需要让任务只在某个时刻执行一次,或者,在某个时刻开始,然后按照某个时间间隔重复执行,简单地说,如果你想让触发器在 2005 年1 月13日,上午 11:23:54秒执行,然后每个隔10秒钟重复执行一次,并且这样重复5 次。那么SimpleTrigger就可以满足你的要求。
4.1.2 CronTrigger
如果你需要像日历那样按日程来触发任务,而不是像SimpleTrigger 那样每隔特定的间隔时间触发,CronTriggers 通常比SimpleTrigger更有用。
使用CronTrigger ,你可以指定诸如“每个周五中午”,或者“每个工作日的 9:30”或者“从每个周一、周三、周五的上午 9:00到上午10:00之间每隔五分钟”这样日程安排来触发。甚至,象SimpleTrigger一样,CronTrigger 也有一个startTime以指定日程从什么时候开始,也有一个(可选的)endTime 以指定何时日程不再继续。
Cron Expressions——Cron表达式
Cron表达式被用来配置CronTrigger 实例。Cron表达式是一个由7 个子表达式组成的字符串。每个子表达式都描述了一个单独的日程细节。这些子表达式用空格分隔,分别表示:
1. Seconds 秒
2. Minutes 分钟
3. Hours 小时
4. Day-of-Month 月中的天
5. Month 月
6. Day-of-Week 周中的天
7. Year (optional field) 年(可选的域)
一个cron表达式的例子字符串为"0 0 12 ? * WED", 这表示“每周三的中午12:00”。单个子表达式可以包含范围或者列表。例如:前面例子中的周中的天这个域(这里是"WED")可以被替换为"MON-FRI", "MON, WED, FRI" 或者甚至"MON-WED,SAT" 。
通配符('*')可以被用来表示域中“每个”可能的值。因此在"Month"域中的*表示每个月,而在Day-Of-Week 域中的*则表示“周中的每一天”。
所有的域中的值都有特定的合法范围,这些值的合法范围相当明显,例如:秒和分域的合法值为0 到59,小时的合法范围是 0 到23,Day-of-Month 中值得合法凡范围是 0到31,但是需要注意不同的月份中的天数不同。月份的合法值是 0 到11。或者用字符串JAN,FEB MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV 及DEC来表示。Days-of-Week可以用1 到7 来表示(1=星期日)或者用字符串SUN, MON, TUE, WED,THU, FRI 和SAT 来表示.
'/' 字符用来表示值的增量,例如, 如果分钟域中放入'0/15' ,它表示“每隔 15分钟,从0 开始”,如果在份中域中使用'3/20' ,则表示“小时中每隔20分钟,从第3 分钟开始”或者另外相同的形式就是'3,23,43' 。
'?'字符可以用在 day-of-month 及day-of-week域中,它用来表示“没有指定值”。这对于需要指定一个或者两个域的值而不需要对其他域进行设置来说相当有用。看下面例子(以及CronTrigger JavaDOC )会更清楚。
'L'字符可以在day-of-month 及day-of-week中使用,这个字符是"last" 的简写,但是在两个域中的意义不同。例如,在day-of-month 域中的"L" 表示这个月的最后一天,即,一月的31日,非闰年的二月的28日。如果它用在day-of-week中,则表示"7" 或者"SAT" 。但是如果在 day-of-week域中,这个字符跟在别的值后面,则表示" 当月的最后的周XXX"。例如:"6L" 或者 "FRIL" 都表示本月的最后一个周五。当使用'L'选项时,最重要的是不要指定列表或者值范围,否则会导致混乱。
'W' 字符用来指定距离给定日最接近的周几(在 day-of-week域中指定)。例如:如果你为day-of-month 域指定为"15W", 则表示“距离月中15号最近的周几”。
'#' 表示表示月中的第几个周几。例如:day-of-week域中的"6#3" 或者 "FRI#3"表示“月中第三个周五”。
下面是一些表达式以及它们的含义,你可以在CronTrigger 的JavaDOC 中找大更多例子。
![](http://static.oschina.net/uploads/space/2013/0801/110209_vM2l_1177247.jpg)
4.2 JobStores
JobStore 负责保持对所有scheduler “工作数据”追踪,这些工作数据包括:job(任务),trigger (触发器),calendar(日历) 等。
4.2.1 RAMJobStore
AMJobStore 是最简单的JobStore,也是性能最好的(根据 CPU时间)。从名字就可以直观地看出,RAMJobStore 将所有的数据都保存在RAM中。这就是为什么它闪电般的快速和如此容易地配置。缺点就是当应用结束时所有的日程信息都会丢失,这意味着RAMJobStore 不能满足Jobs和Triggers的持久性(“non-volatility”)。对于有些应用来说,这是可以接受的,甚至是期望的行为。但是对于其他应用来说,这将是灾难。
为了使用RAMJobStore(假设你使用StdSchedulerFactory),指使简单地将类名org.quartz.simpl.RAMJobStore作为你的quartz的配置值。
4.2.2 JDBCJobStore
数据通过JDBC 保存到数据库可中, 配置要比RAMJobStore 稍微复杂,同时速度也没有那么快。DBCJobStore 几乎可以在任何数据库上工作,它广泛地使用Oracle, MySQL, MS SQLServer2000, HSQLDB, PostreSQL 以及 DB2 。要使用JDBCJobStore ,首先必须创建一套Quartz使用的数据库表,可以在Quartz 的docs/dbTables找到创建库表的SQL脚本。
选择事务的考虑:
如果不想将scheduling命令绑到其他的事务上,那么你可以通过对JobStore使用JobStoreTX来让Quartz帮你管理事务(这是最普遍的选择)。
如果想让Quartz同其他的事务协同工作(例如:J2EE应用服务器中的事务),那么你需要用JobStoreCMT,这样,Quartz就会让应用服务器容器来管理事务。
4.3 Spring集成应用
4.3.1 继承QuartzJobBean
package test.timerTask;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class SayHelloTaskUsingQuartz extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
// TODO Auto-generated method stub
System.out.println("使用Quartz 认为调度: Hello!!");
}
}
4.3.2 注册Spring
4.3.2.1 JobDetailBean
<bean id="sayHelloJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>test.timerTask.SayHelloTaskUsingQuartz</value>
</property>
</bean>
<!-- 关键在如下两个触发器的配置 -->
<!-- 类似于Java的简单触发器 -->
<bean id="helloTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail">
<ref bean="sayHelloJob"/>
</property>
<property name="startDelay">
<value>1000</value>
</property>
<property name="repeatInterval">
<value>3000</value>
</property>
</bean>
<!-- 复杂触发器 -->
<bean id="helloCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<ref bean="sayHelloJob"/>
</property>
<property name="cronExpression">
<!-- 关键在配置此表达式 -->
<value>0 49 15 * * </value>
</property>
</bean>
4.3.2.2 MethodInvokingJobDetailFactoryBean
<bean id="methodInvokingJobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject"><ref bean="financeDAO"/></property>
<property name="targetMethod"><value>confirmOrder</value></property>
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<ref bean="methodInvokingJobDetail"/>
</property>
<property name="cronExpression">
<value>0 0 6,12,20 * * ?</value>
</property>
</bean>
<!-- 总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list><ref local="cronTrigger"/></list>
</property>
<property name="quartzProperties">
<props>
<prop key="org.quartz.threadPool.threadCount">1</prop>
<prop key="org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer">true</prop>
<!--
<prop key="org.quartz.scheduler.instanceName">scheduler</prop>
<prop key="org.quartz.scheduler.wrapJobExecutionInUserTransaction">true</prop>
<prop key="org.quartz.scheduler.rmi.export">false</prop>
<prop key="org.quartz.scheduler.rmi.proxy">false</prop>
<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
<prop key="org.quartz.threadPool.threadCount">5</prop>
<prop key="org.quartz.threadPool.threadPriority">5</prop>
<prop key="org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread">false</prop>
<prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
<prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>
-->
</props>
</bean>
本文中尚未涉及到对spring定时服务进行集群负载均衡配置,应用Quartz框架知识高级部分。