Quartz学习及Spring下实现

Quartz是开源任务调度框架中的姣姣处着,提供了强大的任务调度机制。典型的企业应用调度,比如论坛来说:每半小时生成精华文章的RSS,每天凌晨统计用户的积分排行等。

以上所举的调度场景核心都是以时间点为关注点的调度,其实还包括资源上的调度。比如Web Server在收到请求时,立即创建一个新的线程服务于该请求,这也属于调度的范畴。

Quartz对调度场景进行了高度抽象,提出了三个核心概念,调度器-Scheduler,任务-Job,和触发器-Trigger

http://www.blogjava.net/baoyaer/articles/155645.html文章不错,推荐。

1.调度器-Job

源代码:可见Job只是个接口,有execute()这个方法,在实现类中通过实现execute()方法表示需要执行的任务
package org.quartz;

/**
 * <p>
 * The interface to be implemented by classes which represent a 'job' to be
 * performed.
 * </p>
 * 
 * <p>
 * Instances of <code>Job</code> must have a <code>public</code>
 * no-argument constructor.
 * </p>
 * 
 * <p>
 * <code>JobDataMap</code> provides a mechanism for 'instance member data'
 * that may be required by some implementations of this interface.
 * </p>
 * 
 * @see JobDetail
 * @see StatefulJob
 * @see Trigger
 * @see Scheduler
 * 
 * @author James House
 */
public interface Job {

    /*
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * 
     * Interface.
     * 
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     */

    /**
     * <p>
     * Called by the <code>{@link Scheduler}</code> when a <code>{@link Trigger}</code>
     * fires that is associated with the <code>Job</code>.
     * </p>
     * 
     * <p>
     * The implementation may wish to set a 
     * {@link JobExecutionContext#setResult(Object) result} object on the 
     * {@link JobExecutionContext} before this method exits.  The result itself
     * is meaningless to Quartz, but may be informative to 
     * <code>{@link JobListener}s</code> or 
     * <code>{@link TriggerListener}s</code> that are watching the job's 
     * execution.
     * </p>
     * 
     * @throws JobExecutionException
     *           if there is an exception while executing the job.
     */
    void execute(JobExecutionContext context)
        throws JobExecutionException;

}

2.任务详情-JobDetail

以下为源代码拷取得部分,JobDetail的构造函数表明了通过Job在调度器中组名和Job名来管理Job实例对象。
一句话, JobDetail类管理Job的实现对象。
public JobDetail(String name, String group, Class jobClass) {
        setName(name);
        setGroup(group);
        setJobClass(jobClass);
    }

3.触发器-Trigger



一张图,帮你理解触发器,个人感觉还是CronTrigger实现类使用更广,下面的项目Demo就是用该子类。

4.调度器-Scheduler

代表一个Quartz的独立运行容器,Trigger-触发器和JobDetail-任务详情可以注册到Scheduler中,并且两者拥有各自独立的组和名称。有一篇博文比较实用, http://blog.csdn.net/hongweigg/article/details/6185599,讲述的是在使用Quartz中可能会遇到阻塞的问题。本人结合Quartz代码进行理解,希望对有用的朋友可以有所帮助。

结构图:


Job子接口StatefulJob源代码:
/* 
 * Copyright 2001-2009 Terracotta, Inc. 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
 * use this file except in compliance with the License. You may obtain a copy 
 * of the License at 
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0 
 *   
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
 * License for the specific language governing permissions and limitations 
 * under the License.
 * 
 */

package org.quartz;

/**
 * <p>
 * A marker interface for <code>{@link org.quartz.JobDetail}</code> s that
 * wish to have their state maintained between executions.
 * </p>
 * 
 * <p>
 * <code>StatefulJob</code> instances follow slightly different rules from
 * regular <code>Job</code> instances. The key difference is that their
 * associated <code>{@link JobDataMap}</code> is re-persisted after every
 * execution of the job, thus preserving state for the next execution. The
 * other difference is that stateful jobs are not allowed to execute
 * concurrently, which means new triggers that occur before the completion of
 * the <code>execute(xx)</code> method will be delayed.
 * </p>
 * 
 * @see Job
 * @see JobDetail
 * @see JobDataMap
 * @see Scheduler
 * 
 * @author James House
 */
public interface StatefulJob extends Job {

    /*
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     * 
     * Interface.
     * 
     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     */

}

Job在运行时的信息都会保存在JobDataMap中,通常Job在执行时,默认执行的是无状态的的Job,
每个实例对象都会拥有独立的JobDataMap复制,对JobDataMap的更改不会影响到下次的执行,
所以 无状态的Job可并发执行。
StatefulJob代表有状态的Job,会共享同一个 JobDataMap,每次任务执行对JobDataMap
所做的更改都会保存下来,所以有状态的Job不能并发执行。只有上次的StatefulJob执行
完毕下次的StatefulJob才能执行,否则将会阻塞等待。
在Spring里面可以通过concurrent属相来表明有无状态,concurrent=true表示可并发,false表示不能并发。下面的Demo中会体现这点。
接口网络繁忙时,接口状态查询任务会占用很长的时间,Spring定时任务默认都是并发执行的,不会等待上一次任务执行结束只要间隔时间到就会执行。接口状 态查询任务每5分钟执行一次,假如每次都执行1小时的话,其他任务就会被阻塞。因为Quartz的线程都被接口状态查询任务占用了。其他任务只有等待。 -------------先记着,要万一能用上呢。偷笑

#####################################################################################################################


1.引Quartz的Maven库

<dependency>
			  <groupId>org.quartz-scheduler</groupId>
			  <artifactId>quartz</artifactId>
			  <version>1.8.4</version>
		</dependency>


2.Quartz的配置文件

2.1.首先在web.xml中引入配置文件sample-job.xml




2.2.sample-job.xml配置文件:
<!-- 任务调度  每2秒钟均执行一次调度-->
	<!-- 要调用的工作类 -->
	 <bean id="quartzJob" class="com.suning.sample.job.QuartzJob" />
	 
	<!-- 定义调用对象和调用对象的方法 -->
	<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	 	<!-- 调用的类 -->
	 	<property name="targetObject">
	 		<ref bean="quartzJob" />
	 	</property>
	 	<!-- 调用类中的方法 -->
	 	<property name="targetMethod">
	 		<value>work</value>
	 	</property>
	 	<!--  true:可并发  否则不可并发 -->
	 	<property name="concurrent">
	 		<value>true</value>
	 	</property>
	</bean>
	
	<!-- 定义触发时间 -->
	<bean id="doTime" class="org.springframework.scheduling.quartz.CronTriggerBean">
		<property name="jobDetail">
			<ref bean="jobDetail" />
		</property>
		<!--  延迟触发 -->
		<property name="startDelay">
			<value>3000</value>
		</property>
		<!--  cron表达式 -->
		<property name="cronExpression">
			<value>0/2 * * * * ?</value>
		</property>
	</bean>
	
	<!--  总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序  -->
	<bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="triggers">
			<list>
				<ref bean="doTime" />
			</list>
		</property>
	</bean>


2.2.1:MethodInvokingJobDetailFactoryBean

为了满足Quartz Job接口的规范,需要一个类实现Job接口,重写execute()方法来定义需要调度的方法,然后JobDetail通过构造方法管理Job的实现类,这样子不符合Spring的注入风格,笔者很喜欢set的Spring注入方式呢。所以Spring通过MethodInvokingJobDetailFactoryBean类来管理要调度的Bean。有两个重要的property,targetObject和targetMethod来分别表示要调用的类和方法,这样子来就可以不必实现Job接口,而直接写要调度的Bean。
concurrent属性表示quartzJob#work()方法是否可并发,默认为true。true:代表可并发,false:不可并发。

2.2.2:创建触发器-Trigger

前面介绍过有两种实现类,SimpleTriggerBean和CronTriggerBean,后者支持cron表示式定义触发时间。
重要的property是jobDetail和cronExpression。下面讲述cron时间表达式规则:

2.2.3:Cron表达式规则
Quartz使用类似于Linux下的Cron表达式定义时间规则,Cron表达式由6或7个由空格分隔的时间字段组成,如表1所示:

表1: Cron表达式时间字段

位置

时间域名

允许值

允许的特殊字符

1

0-59

, - * /

2

分钟

0-59

, - * /

3

小时

0-23

, - * /

4

日期

1-31

, - * ? / L W C

5

月份

1-12

, - * /

6

星期

1-7

, - * ? / L C #

7

年(可选)

空值1970-2099

, - * /

Cron表达式的时间字段除允许设置数值外,还可使用一些特殊的字符,提供列表、范围、通配符等功能,如下:

●星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如,*在分钟字段时,表示“每分钟”;

●问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;

●减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;

●逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;

●斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;

●L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五;

●W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围;

●LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;

●井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;

● C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。

Cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。


表2下面给出一些完整的Cron表示式的实例:

表2 Cron表示式示例

表示式

说明

"0 0 12 * * ? "

每天12点运行

"0 15 10 ? * *"

每天10:15运行

"0 15 10 * * ?"

每天10:15运行

"0 15 10 * * ? *"

每天10:15运行

"0 15 10 * * ? 2008"

在2008年的每天10:15运行

"0 * 14 * * ?"

每天14点到15点之间每分钟运行一次,开始于14:00,结束于14:59。

"0 0/5 14 * * ?"

每天14点到15点每5分钟运行一次,开始于14:00,结束于14:55。

"0 0/5 14,18 * * ?"

每天14点到15点每5分钟运行一次,此外每天18点到19点每5钟也运行一次。

"0 0-5 14 * * ?"

每天14:00点到14:05,每分钟运行一次。

"0 10,44 14 ? 3 WED"

3月每周三的14:10分到14:44,每分钟运行一次。

"0 15 10 ? * MON-FRI"

每周一,二,三,四,五的10:15分运行。

"0 15 10 15 * ?"

每月15日10:15分运行。

"0 15 10 L * ?"

每月最后一天10:15分运行。

"0 15 10 ? * 6L"

每月最后一个星期五10:15分运行。

"0 15 10 ? * 6L 2007-2009"

在2007,2008,2009年每个月的最后一个星期五的10:15分运行。

"0 15 10 ? * 6#3"

每月第三个星期五的10:15分运行。



3.QuartzJob-被调用的类

package com.suning.sample.job;

import java.util.Date;

/**
 * 
 * 被调度的任务类<br> 
 * 〈功能详细描述〉
 *
 * @author 13073386
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class QuartzJob {
    public void work(){
        System.out.println("it begins to job" + "(" + new Date() + ")");
    }

}

4.实现的结果-2s调用一次



Cron规则是两秒调用一次,根据实际需求来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值