现在的位置: 首页 > 综合 > 正文

使用Java Timer以及quartz调度任务

2013年12月02日 ⁄ 综合 ⁄ 共 11902字 ⁄ 字号 评论关闭
 

Spring的定时器的使用
使用Java Timer调度任务
从Java 1.3开始,Java SDK就通过java.util.Timer类提供了基本的调度功能。这个类允许你调度一个任务(通过java.util.TimerTask子类定义)按任意周期运行。
创建一个定时器任务
使用Java Timer来调度发送注册报表邮件的第一步是从java.util.TimerTask中派生出邮件任务,如程序清单7.2所示。
 程序清单7.2 一个用于发送注册报表邮件的定时器任务
public class EmailReportTask extends TimerTask {
    public EmailReportTask() {}
    public void run() {
      courseService.sendCourseEnrollmentReport();
    }
    private CourseService courseService;
    public void setCourseService(CourseService courseService) {
      this.courseService = courseService;
    }
}
n

run()方法定义了当任务运行时该做什么。在上面 的例子中,它调用CourseService的sendCourseEnrollmentReport()方法(见程序清单7.1)来发送注册报表邮件。 CourseService是通过依赖注入方式提供给EmailReportTask的。
按以下方式在Spring配置文件中声明EmailReportTask:
<bean id="reportTimerTask"
       class="com.springinaction.training.schedule.EmailReportTask">
    <property name="courseService">
      <ref bean="courseService"/>
    </property>
</bean>
这个声明本身只是将EmailReportTask放到应用上下文中,并在courseService属性中装配courseService Bean。在你调度它之前,它不会做任何有用的事。
调度定时器任务
Spring的ScheduledTimerTask定义了一个定时器任务的运行周期。既然课程主任要求每天向她发送注册报表,你应该以如下方式装配一个ScheduledTimerTask:
<bean id="scheduledReportTask"
       class="org.springframework.scheduling.timer.ScheduledTimerTask">
    <property name="timerTask">
      <ref bean="reportTimerTask"/>
    </property>
    <property name="period">
      <value>86400000</value>
    </property>
</bean>
属性timerTask告诉 ScheduledTimerTask运行哪个TimerTask。在这里,该属性装配了指向reportTimerTask的一个引用,它就是 EmailReportTask。属性period告诉ScheduledTimerTask以怎样的频度调用TimerTask的run()方法。这个 属性以毫秒作为单位,它被设置为86400000,指定这个任务应该每24小时运行一次。
启动定时器
最后一步是启动定时器。Spring的TimerFactoryBean负责启动定时任务。按以下方式在Spring配置文件中声明它:
<bean class="org.springframework.scheduling.timer.TimerFactoryBean">
    <property name="scheduledTimerTasks">
      <list>
        <ref bean="scheduledReportTask"/>
     </list>
    </property>
</bean>
属性scheduledTimerTasks要求一个需要启动的定时器任务的列表。既然你现在只有一个定时器任务,这个列表中只包含一个指向scheduledReportTask Bean的引用。
遗憾的是,即使这个任务已经能够每隔24小时运行一 次了,在这里你无法指定它应该在一天中的哪个时间点执行。ScheduledTimerTask有一个delay属性,允许你指定当任务第一次运行之前应 该等待多久。例如,要将EmailReportTask的第一次运行延迟1小时,可以按照以下方式进行配置:
<bean id="scheduledReportTask"
       class="org.springframework.scheduling.timer.ScheduledTimerTask">
    <property name="timerTask">
      <ref bean="reportTimerTask"/>
    </property>
    <property name="period">
      <value>86400000</value>
    </property>
    <property name="delay">
      <value>3600000</value>
    </property>
    </bean>
即使使用deplay属性,EmailReportTask的第一次运行时间仍然是相对于应用程序的启动时间的。怎样才能做到如课程主任所要求的在每天早晨6:00发送邮件(而不是在早晨5:00启动应用程序)呢?
遗憾的是,这是Java Timer的一个局限性。你可以指定任务执行的频度,但你无法精确指定它何时运行。为了能够精确指定何时发送电子邮件,你需要使用Quartz调度器。

Quartz调度器为调度工作提供了更丰富的支持。和Java定时器一样,可以使用Quartz来每隔多少毫秒执行一个工作。但Quartz比Java Timer更先进之处在于它允许你调度一个工作在某个特定的时间或日期执行。
关于Quartz的更多信息,可以访问Quartz位于http://www.opensymphony.com/quartz的主页。
让我们从定义发送报表邮件的工作开始使用Quartz:
创建一个工作
定义Quartz工作的第一步是创建一个类来定义工作。要做到这一点,你需要从Spring的QuartzJobBean中派生子类,如程序清单7.3所示:
 程序清单7.3 定义一个Quartz工作
public class EmailReportJob extends QuartzJobBean {
    public EmailReportJob() {}
    protected void executeInternal(JobExecutionContext context)
        throws JobExecutionException {


      courseService.sendCourseEnrollmentReport();
    }
    private CourseService courseService;
    public void setCourseService(CourseService courseService) {
      this.courseService = courseService;
    }
}
n

QuartzJobBean是Quartz中与 Java的TimerTask等价的类。它实现了org.quartz.Job接口。executeInternal()方法定义了当预定的时刻来临时应 该执行哪些动作。在这里,正如EmailReportTask,你只是简单地调用了courseService属性的 sendCourseEnrollmentReport()方法。
在Spring配置文件中按以下方式声明这个工作:
<bean id="reportJob"
       class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass">
      <value>com.springinaction.training.
           ➥schedule.EmailReportJob</value>
    </property>
    <property name="jobDataAsMap">
      <map>
        <entry key="courseService">
          <ref bean="courseService"/>
        </entry>
      </map>
    </property>
</bean>
值得注意的是,在这里你并没有直接声明一个 EmailReportJob Bean,而是声明了一个JobDetailBean。这是使用Quartz时的一个特点。JobDetailBean是Quartz的 org.quartz.JobDetail的子类,它要求通过jobClass属性来设置一个Job对象。
使用Quartz的JobDetail中的另一个特 别之处是EmailReportJob的courseService属性是间接设置的。JobDetail的jobDataAsMap属性接受一个 java.util.Map,其中包含了需要设置给jobClass的各种属性。在这里,这个map包含了一个指向courseService Bean的引用,它的键值为courseService。当JobDetailBean实例化时,它会将courseService Bean注入到EmailReportJob的courseService属性中。
调度工作
现在工作已经被定义好了,接下来你需要调度这个工 作。Quartz的org.quartz.Trigger类描述了何时及以怎样的频度运行一个Quartz工作。Spring提供了两个触发 器,SimpleTriggerBean和CronTriggerBean。你应该使用哪个触发器?让我们分别考察一下这两个触发器,首先从 SimpleTriggerBean开始。
SimpleTriggerBean与 ScheduledTimerTask类似。你可以用它来指定一个工作应该以怎样的频度运行,以及(可选地)在第一次运行工作之前应该等待多久。例如,要 调度报表工作每24小时运行一次,第一次在1小时之后开始运行,可以按照以下方式进行声明:
<bean id="simpleReportTrigger"
       class="org.springframework.scheduling.quartz.SimpleTriggerBean">
    <property name="jobDetail">
      <ref bean="reportJob"/>
    </property>
    <property name="startDelay">
      <value>3600000</value>
    </property>
    <property name="repeatInterval">
      <value>86400000</value>
    </property>
</bean>
属性jobDetail装配了将要被调度的工作,在 这个例子中是reportJob Bean。属性repeatInterval告诉触发器以怎样的频度运行这个工作(以毫秒作为单位)。这里,我们设置它为86400000,因此每隔24 小时它会被触发一次。你也可以选择设置startDelay属性来延迟工作的第一次执行。我们设置它为3600000,因此在第一次触发之前它会等待1小 时。
调度一个cron工作
尽管你可能认为SimpleTriggerBean 适用于大多数应用,但它仍然不能满足发送注册报表邮件的需求。正如ScheduledTimerTask,你只能指定工作执行的频度,而不能准确指定它于 何时运行。因此,你无法使用SimpleTriggerBean在每天早晨6:00给课程主任发送注册报表邮件。
然而,CronTriggerBean允许你更精确 地控制任务的运行时间。如果你对Unix的cron工具很熟悉,则会觉得CronTriggerBean很亲切。你不是定义工作的执行频度,而是指定工作 的准确运行时间(和日期)。例如,要在每天早上6:00运行报表工作,可以按照以下方式声明一个CronTriggerBean:
<bean id="cronReportTrigger"
       class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail">
      <ref bean="reportJob"/>
    </property>
    <property name="cronExpression">
      <value>0 0 6 * * ?</value>
    </property>
</bean>
和SimpleTriggerBean一 样,jobDetail属性告诉触发器调度哪个工作。这里我们又一次装配了一个reportJob Bean。属性cronExpression告诉触发器何时触发。如果你不熟悉cron,这个属性可能看上去有点神秘,因此让我们进一步考察一下这个属 性。
一个cron表达式有至少6个(也可能是7个)由空格分隔的时间元素。从左至右,这些元素的定义如下:
1.秒(0–59)
2.分钟(0–59)
3.小时(0–23)
4.月份中的日期(1–31)
5.月份(1–12或JAN–DEC)
6.星期中的日期(1–7或SUN–SAT)
7.年份(1970–2099)
每一个元素都可以显式地规定一个值(如6),一个区 间(如9-12),一个列表(如9,11,13)或一个通配符(如*)。“月份中的日期”和“星期中的日期”这两个元素是互斥的,因此应该通过设置一个问 号(?)来表明你不想设置的那个字段。表7.1中显示了一些cron表达式的例子和它们的意义:
表7.1                                            一些cron表达式的例子
表 达 式
意    义
0 0 10,14,16 * * ?
每天上午10点,下午2点和下午4点
0 0,15,30,45 * 1-10 * ?
每月前10天每隔15分钟
30 0 0 1 1 ? 2012
在2012年1月1日午夜过30秒时
0 0 8-5 ? * MON-FRI
每个工作日的工作时间
对于cronReportTrigger,我们设置cronExpression为0 0 6 * * ?可以把它读作“在任何月份任何日期(不管是星期几)的6时0分0秒执行触发器。”换句话说,这个触发器会在每天早晨6:00执行。
使用CronTriggerBean完全能够满足课程主任的期望了。现在剩下要做的只是启动这个工作了。
启动工作
Spring的SchedulerFactoryBean是Quartz中与TimerFactoryBean等价的类。按照如下方式在Spring配置文件中声明它:
   <bean class="org.springframework.scheduling.
         ➥quartz.SchedulerFactoryBean">
    <property name="triggers">
      <list>
        <ref bean="cronReportTrigger"/>
      </list>
    </property>
</bean>
属性triggers接受一组触发器。由于目前只有一个触发器,因此只需简单地装配一个包含cronReportTrigger Bean的一个引用的列表即可。
现在,你已经实现了调度发送注册报表邮件的需求。但在这个过程中,你做了一些额外的工作。在开始新的话题之前,首先让我们看一下如何通过更简单一些的方式调度报表邮件.
按照上面的配置完成不一定能启动定时任务,下面的方法我实验过了,是可以的,哈哈。
Java代码

  1. <?xml version="1.0" encoding="UTF-8"?>   
  2. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">   
  3. <beans>   
  4. <!--起动Bean-->   
  5.  <bean id="z" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">   
  6.     <property name="triggers">   
  7.         <list>   
  8.        
  9.             <ref bean="cronReportTrigger"/>   
  10.         </list>   
  11.     </property>   
  12.  </bean>    
  13. <!--实际的工作Bean-->   
  14.   <bean id="courseService" class="com.spring.helloworld.CourseService"></bean>   
  15. <!--jobBean用于设定启动时运用的Bean与方法-->   
  16.  <bean id="scheduledReportJobDetail"     
  17. class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">   
  18.      <property name="targetObject">   
  19.         <ref  bean="courseService"/>     
  20.      </property>   
  21.      <property name="targetMethod">   
  22.          <value>sendCourseEnrollmentReport</value>   
  23.      </property>   
  24.  </bean>   
  25. <!--定时器设定起动频率&启动时间我设的是每5秒起动一次 (0 0  4 * * ?每日四点起动....)-->   
  26.  <bean id="cronReportTrigger"    
  27. class="org.springframework.scheduling.quartz.CronTriggerBean">   
  28.  <property  name="jobDetail"><ref bean="scheduledReportJobDetail"/>   
  29.  </property>   
  30.  <property name="cronExpression"><value>10,15,20,25,30,35,40,45,50,55 * * * * ?</value>     
  31. </property>   
  32.  </bean>   
  33.        
  34. </beans>  

测试

Java代码
package com.spring.helloworld;   

  1.   
  2.   
  3. import java.io.FileNotFoundException;   
  4.   
  5. import org.springframework.beans.BeansException;   
  6. import org.springframework.beans.factory.BeanFactory;   
  7. import org.springframework.beans.factory.xml.XmlBeanFactory;   
  8. import org.springframework.core.io.FileSystemResource;   
  9.   
  10.   
  11. import com.spring.hellow.LogInit;   
  12. import com.spring.hellow.QuestException;   
  13.   
  14.   
  15. public class TaskApp {   
  16.   
  17.     public static void main(String[] args) throws BeansException, FileNotFoundException, QuestException, InterruptedException {   
  18.         new LogInit("WEB-INF/classes/com/spring/helloworld/log4j.properties");   
  19.         BeanFactory factory = new XmlBeanFactory(new FileSystemResource(   
  20.         "WEB-INF/classes/" +   
  21.         "com/spring/helloworld/" +   
  22.         "helloworld.xml"));   
  23.         factory.getBean("z");//为什么必须用这个方法触发呢不触发为什么不行呢?   
  24.   
  25.   
  26.     }   
  27.        
  28.   
  29. }  

评论

julycool 2007-09-05
设置lazy-init为false

Godlikeme 2006-12-24
factory.getBean("z");//为什么必须用这个方法触发呢不触发为什么不行呢?
因为这个bean没有加载,scheduler没有实例化。如果自定义scheduler,还需要显示调用scheduler.start().或者再配置文件中声明。

再介绍一下quartz在web中的使用方法:
1、到http://www.opensymphony.com/quartz/
下载稳定的版本(我用的是1.4.5版)。
2、解压至任何目录,目录中有简单的教程、例题、核心包及辅助包,请检查你的目录。
3、开始创建任务,一个任务类需实现org.quartz.Job接口;在任务类中定义任务调用方法public void execute(JobExecutionContext context) throws JobExecutionException {},主要代码就在execute方法中写。
4、写好任务后,开始定义任务描述文件,可结合http://www.quartzscheduler.org/ns/ ...eduling_data_1_1.xsd和自带例题进行定义(examples/jobs.xml),建议最好从examples/jobs.xml进行更改;注意任务需要被激活,故一个成功配置的任务会包含任务描述和触发描述,请参考相关文件。
5、Quartz在容器下是通Servlet的初始化进行启动的,故需要在你的web部署描述文件(即web.xml)中进行定义,在web.xml中加入:

        <servlet>
          <servlet-name>QuartzInitializer</servlet-name>
          <display-name>Quartz Initializer Servlet</display-name>
          <servlet-class>org.quartz.ee.servlet.QuartzInitializerServlet</servlet-class>
          <load-on-startup>1</load-on-startup>
        </servlet>  

6、Quartz引擎的需要初始化,它一般通过一个名为quartz.properties的属性文件进行参数定义;文件的具体定义方法在文件里有详细说明。建议从docs/config/example_quartz.properties文件进行更改;要注意是文件中对于任务数据持久化那段的设置,我使用的是基于RAM的存储方式。
7、基本上大功告成,将quartz.properties和jobs.xml复制到WEB-INF/classes目录下;将quartz.jar及其他辅助jar(都在解压文件夹的lib目录下,如果你的容器环境中已包含这些库就无需再增加)到WEB-INF/lib下。
8、启动容器,按照你设定的计划观察是否运行正确。

下面网站有关于quartz更多的介绍

http://blog.csdn.net/zdsxj2002/archive/2006/01/16/581008.aspx

 

 

抱歉!评论已关闭.