使用Quartz或Timer完成时序调度工作

本文介绍了在Spring中如何使用Quartz和Timer进行定时任务调度。Quartz通过Triggers、Jobs和JobDetail实现灵活的调度,而Timer则提供了一种简单的方法来执行周期性任务。Spring提供了JobDetailBean和MethodInvokingJobDetailFactoryBean等工具,简化了Quartz的集成。对于Timer,可以使用TimerTask和TimerFactoryBean来创建和管理定时任务。文章还提到了cron表达式在Quartz中的应用,以及如何设置定时任务的执行时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 使用QuartzTimer完成时序调度工作

简介

Spring提供了支持时序调度(译者注:Scheduling,下同)的整合类.现在, Spring支持内置于1.3版本以来的JDK中的TimerQuartz Scheduler(http://www.quartzscheduler.org)。 两个时序调度器通过FactoryBean建立,保持着可选的对Timers或者Triggers的引用。更进一步的, 对于Quartz SchedulerTimer两者存在一个方便的类允许你调用目标对象(类似于通常的MethodInvokingFactoryBeans)上的某个方法

第一种方式: 使用OpenSymphony Quartz Scheduler

Quartz使用Triggers,JobsJobDetail来实现时序调度中的各种工作,实现每隔多少毫秒执行一个工作。但QuartzJava Timer更先进之处在于它允许你调度一个工作在某个特定的时间或日期执行。为了了解Quartz背后的种种基本观点,你可以移步至http://www.opensymphony.com/quartz。 为了方便的使用,Spring提供了几个类在基于Spring的应用中来简化对Quartz的使用。

111创建一个工作

定义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;

    }

  }

QuartzJobBeanQuartz中与 JavaTimerTask等价的类。它实现了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时的一个特点。JobDetailBeanQuartz org.quartz.JobDetail的子类,它要求通过jobClass属性来设置一个Job对象。

使用QuartzJobDetail中的另一个特 别之处是EmailReportJobcourseService属性是间接设置的。JobDetailjobDataAsMap属性接受一个 java.util.Map,其中包含了需要设置给jobClass的各种属性。在这里,这个map包含了一个指向courseService Bean的引用,它的键值为courseService。当JobDetailBean实例化时,它会将courseService Bean注入到EmailReportJobcourseService属性中。

11使用JobDetailBean,创建一个工作

JobDetail 对象包括了运行一个job所需要的所有信息。 于是Spring提供了一个所谓的JobDetailBean使得JobDetail拥有了一个真实的,有意义的默认值。让我们来看个例子:

<bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean">
  <property name="jobClass">
    <value>example.ExampleJob</value>
  </property>
  <property name="jobDataAsMap">
    <map>
      <entry key="timeout"><value>5</value></entry>
    </map>
  </property>
</bean>

Job detail bean拥有所有运行job(ExampleJob)的必要信息。通过jobdata map来制定timeout Jobdata map可以通过JobExecutionContext(在运行时刻传递给你)来得到, 但是JobDetailBean也把从jobdata map中得到的属性映射到实际job中的属性中去。 所以,如果ExampleJob中包含一个名为timeout的属性,JobDetailBean将自动为它赋值:

package example;
public class ExampleJob extends QuartzJobBean {
private int timeout;
 /**
   * Setter called after the ExampleJob is instantiated
   * with the value from the JobDetailBean (5)
   */ 
  public void setTimeout(int timeout) {
    this.timeout = timeout;
  }
  protected void executeInternal(JobExecutionContext ctx)
  throws JobExecutionException {
      // do the actual work
   }
}

所有Job detail bean中的一些其他的设定对你来说也是可以同样设置的.

注意:使用namegroup属性,你可以修改job在哪一个组下运行和使用什么名称。 默认情况下,job的名称等于job detai bean的名称(在上面的例子中为exampleJob)。

1.2. 使用MethodInvokingJobDetailFactoryBean

通常情况下,你只需要调用特定对象上的一个方法。你可以使用MethodInvokingJobDetailFactoryBean准确的做到这一点:

<bean id="methodInvokingJobDetail" 
  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject"><ref bean="exampleBusinessObject"/></property>
    <property name="targetMethod"><value>doIt</value></property>
</bean>

上面例子将导致exampleBusinessObject中的doIt方法被调用(如下):

public class BusinessObject {
 // properties and collaborators
 public void doIt() {
    // do the actual work
  }
}
<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/>

使用MethodInvokingJobDetailFactoryBean你不需要创建只有一行代码且只调用一个方法的job, 你只需要创建真实的业务对象来包装具体的细节的对象。

默认情况下,Quartz Jobs是无状态的,可能导致jobs之间互相的影响。如果你为相同的JobDetail指定两个触发器, 很可能当第一个job完成之前,第二个job就开始了。如果JobDetail对象实现了Stateful接口,就不会发生这样的事情。 第二个job将不会在第一个job完成之前开始。为了使得jobs不并发运行,设置MethodInvokingJobDetailFactoryBean中的concurrent标记为false

<bean id="methodInvokingJobDetail"  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject">
<ref bean="exampleBusinessObject"/>
</property>
<property name="targetMethod">
<value>doIt</value>
</property>
</bean>                

注意:默认情况下,jobs在并行的方式下运行。

13. 使用triggersSchedulerFactoryBean来包装任务

我们已经创建了job details,jobs。我们回顾了允许你调用特定对象上某一个方法的便捷的bean。 当然我们仍需要调度这些jobs。这需要使用triggersSchedulerFactoryBean来完成。 Quartz自带一些可供使用的triggersSpring提供两个子类triggers,分别为CronTriggerBeanSimpleTriggerBean

Triggers也需要被调度。Spring提供SchedulerFactoryBean来暴露一些属性来设置triggersSchedulerFactoryBean负责调度那些实际的triggers

两个例子:

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
  <property name="jobDetail">
    <!-- see the example of method invoking job above -->    
    <ref bean="methodInvokingJobDetail"/>
  </property>
 <property name="startDelay">
    <!-- 10 seconds -->
<value>10000</value>  
</property>
 <property name="repeatInterval">
    <!-- repeat every 50 seconds  -->
    <value>50000</value>    
 </property>
</bean>

  
    
  
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
  <property name="jobDetail">
    <ref bean="exampleJob"/>
  </property>
  <property name="cronExpression">
    <!-- run every morning at 6 am -->
    <value>0 6 * * 1</value>
  </property>
</bean>
                       

现在我们创建了两个triggers,其中一个开始延迟10秒以后每50秒运行一次,另一个每天早上6点钟运行。 我们需要创建一个SchedulerFactoryBean来最终实现上述的一切:

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  <property name="triggers">
    <list>
      <ref local="cronTrigger"/>
      <ref local="simpleTrigger"/>
    </list>
  </property>
</bean>
                       

更多的一些属性你可以通过SchedulerFactoryBean来设置,例如job details使用的Calendars,用来订制Quartz的一些属性以及其它。 你可以看相应的JavaDOC(http://www.springframework.org/docs/api/org/springframework/scheduling/quartz/SchedulerFactoryBean.html)来了解进一步的信息。

第二种方式: 使用JDK Timer支持类

另外一个调度任务的途径是使用JDK Timer对象。更多的关于Timers的信息可以在这里http://java.sun.com/docs/books/tutorial/essential/threads/timer.html找到。 上面讨论的概念仍可以应用于Timer的支持。你可以创建定制的timer或者调用某些方法的timer。 包装timers的工作由TimerFactoryBean完成。

2.1. 创建定制的timers

你可以使用TimerTask创建定制的timer tasks,类似于Quartz中的jobs:

public class CheckEmailAddresses extends TimerTask {
private List emailAddresses;
public void setEmailAddresses(List emailAddresses) {
    this.emailAddresses = emailAddresses;
  }
public void run() {
    // iterate over all email addresses and archive them
  }
}

包装它是简单的:

<bean id="checkEmail" class="examples.CheckEmailAddress">
  <property name="emailAddresses">
    <list>
      <value>test@springframework.org</value>
      <value>foo@bar.com</value>
      <value>john@doe.net</value>
    </list>
  </property>
</bean>

  
    
  
<bean id="scheduledTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
  <!-- wait 10 seconds before starting repeated execution -->
  <property name="delay">
    <value>10000</value>
  </property>
  <!-- run every 50 seconds -->
  <property name="period">
    <value>50000</value>
  </property>
  <property name="timerTask">
    <ref local="checkEmail"/>
  </property>
</bean>
                       

2.2. 使用MethodInvokingTimerTaskFactoryBean

就像Quartz的支持一样,Timer的支持也有一个组件允许你周期性地调用一个方法:

<bean id="methodInvokingTask" 
  class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean">
    <property name="targetObject">
<ref bean="exampleBusinessObject"/>
</property>
<property name="targetMethod">
<value>doIt</value>
</property>
</bean>

上面的例子将会导致exampleBusinessObject上的doIt方法被调用(如下):

public class BusinessObject {
  
  // properties and collaborators
  
  public void doIt() {
    // do the actual work
  }
}

把上面例子中提到ScheduledTimerTask的引用改为methodInvokingTask将导致该task被执行。

2.3. 包装:使用TimerFactoryBean来建立tasks

TimerFactoryBean类似于QuartzSchedulerFactoryBean,都是服务于一个目的:建立起实际的时序调度。 TimerFactoryBean建立一个实际的Timer来调度它引用的那些tasks。你可以指定它是否使用一个守护线程。

<bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean">
  <property name="scheduledTimerTasks">
    <list>
      <!-- see the example above -->
      <ref local="scheduledTask"/>
    </list>
  </property>
</bean>
                       

 

补充内容:

一个cron表达式有至少6个(也可能是7个)由空格分隔的时间元素。从左至右,这些元素的定义如下:

1.秒(059

2.分钟(059

3.小时(023

4.月份中的日期(131

5.月份(112JANDEC

6.星期中的日期(17SUNSAT

7.年份(19702099

每一个元素都可以显式地规定一个值(如6),一个区 间(如9-12),一个列表(如91113)或一个通配符(如*)。“月份中的日期”和“星期中的日期”这两个元素是互斥的,因此应该通过设置一个问 号(?)来表明你不想设置的那个字段。表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,我们设置cronExpression0 0 6 * * ?可以把它读作“在任何月份任何日期(不管是星期几)的600秒执行触发器。”换句话说,这个触发器会在每天早晨6:00执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值