7.3.1 使用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调度器。
7.3.2 使用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的一个引用的列表即可。
现在,你已经实现了调度发送注册报表邮件的需求。但在这个过程中,你做了一些额外的工作。在开始新的话题之前,首先让我们看一下如何通过更简单一些的方式调度报表邮件。
分享到:
相关推荐
在Oracle数据库中,调度任务(Jobs)是一种自动化执行SQL脚本或PL/SQL块的机制,这极大地提高了数据库管理员的工作效率。Oracle Jobs可以按照预设的时间间隔或特定日期执行,适用于执行备份、清理、统计更新等常规...
《C#定时调度任务服务构建详解》 在IT行业中,定时调度任务服务是不可或缺的一部分,它使得系统能够按预设的时间间隔自动执行某些任务,提高了工作效率并降低了人为操作的复杂性。本文将深入探讨如何利用C#语言,...
分布式调度任务XXL-JOB架构图知识点 分布式调度任务XXL-JOB架构图是一种基于quartz调度器的分布式任务调度框架,旨在解决大规模任务的调度和执行问题。下面是该架构图中的关键知识点: 1. 分布式调度中心:XXL-JOB...
"DataWorks调度任务迁移概述" DataWorks调度任务迁移是指将DataWorks工作空间中的调度任务迁移到新的环境中,以便更好地管理和维护这些任务。该技术创新为DataWorks工作空间迁移提供了强大的解决方案。 数据备份是...
这是一个基于Java的惊讶网站统一调度任务平台设计,包含193个文件。主要文件类型包括116个Java源文件、27个JSP文件、11个XML文件、8个JavaScript文件、5个CSS文件、3个Shell脚本文件、3个TLD文件、2个map文件、2个...
本篇文章将深入探讨如何在Spring Boot中实现调度任务的动态管理,这对于构建能够根据业务需求变化灵活调整的任务执行系统至关重要。 首先,我们要了解Spring Boot中用于任务调度的核心组件——`@Scheduled`注解和`...
Linux 操作系统内核编程实现调度任务 Linux 操作系统内核编程实现调度任务是指在 Linux 操作系统中实现调度任务的过程。调度任务是指在特定的时间点执行某些操作的任务。这些任务可以是周期性的,也可以是一次性的...
### IBM TSM 调度任务解析 #### 一、TSM调度任务基本概念 IBM Tivoli Storage Manager (简称TSM) 是一款强大的数据保护和存储管理解决方案,旨在简化数据备份、恢复以及数据归档的过程。TSM的调度任务功能是其核心...
动态调度任务是许多系统中不可或缺的一部分,它可以自动执行预定的任务,如数据同步、报表生成等。本文将深入探讨如何使用SpringBoot来实现动态调度任务。 首先,SpringBoot集成Quartz或Spring Task等任务调度库...
Java语言定时调度任务是指在Java程序中,根据预定的时间点、间隔时间或执行次数来执行特定任务的过程。定时调度任务在现代Web应用中是不可或缺的功能,尤其在需要周期性执行任务的各种业务系统中显得尤为重要。 ...
定时调度任务说明
Java提供了多种方式来实现定时调度任务,本篇将深入探讨这些实现方法。 1. **Java内置定时器:java.util.Timer** Java标准库提供了一个`Timer`类,它允许程序员安排在未来某个时间执行一个任务,或者定期重复执行...
在这个基于"XXL-Job的调度任务服务.zip"的课程设计中,我们可以深入学习到如何使用Java实现一个分布式任务调度系统。以下是关于XXL-Job和相关Java技术的知识点详解: 1. **XXL-Job核心概念** - **Job执行器...
配置定时计划,以及定时调度任务, 调度配置工具 ORM基于Dapper实现 日志采用Nlog实现 将程序打包为桌面安装程序,采用Microsoft Visual Studio 2010、2015、2017 Installer Projects实现 调度服务实现 采用Quartz...
1、明确水电站水库中长期调度任务及所需资料; 2、进行水库长系列常规调度模拟计算,绘制长系列模型求解结果的入库流量、出库流量和库水位过程线的图形,并统计年均入库流量、出库流量、弃水流量、水位、水头、出力...
基于springboot的任务调度技术quartz简单的实现demo,根目录有数据表,需要配置到您自己的数据库中,之后点击运行即可,比较有趣的是其中会用到一些反射知识,还有难点就是要在调度任务执行的类上@service添加类的...
- **Quartz简介**:Quartz是一个开源的作业调度框架,支持复杂的调度策略,如按日期、时间间隔或CRON表达式调度任务。 - **Spring与Quartz集成**:Spring通过`org.springframework.scheduling.quartz`包提供了一种...
HEFT算法的基本思想是基于层次结构的最早完成时间,它首先将整个任务图分成多个层次,然后在每个层次内独立地调度任务。这种分层的方法有助于减少跨层通信的开销,因为同一层内的任务通常具有较高的数据依赖性,而跨...
云计算资源调度问题是如何将任务流中的各个任务合理地分配到云计算平台的资源上,很好地满足用户的需求。图2是一个云计算资源调度的实例,其中t4,t5以及t7分配到计算资源r1上运行,t1,t2以及t6分配到计算资源r2上...
Quartz调度任务学习(快速开发入门),帮助你快速掌握Quartz的开发,使用性较强