一、java.util.Timer
在Java中有一個任務處理類java.util.Timer,非常方便于處理由時間觸發的事件任務,只需建立一個繼承java.util.TimerTask的子類,重載父類的run()方法實現具體的任務,然后調用Timer的public void schedule(TimerTask task, long delay, long period)方法實現任務的調度。
但是這種方法只能實現簡單的任務調度,不能滿足任務調度時間比較復雜的需求。比如希望系統在每周的工作日的8:00時向系統用戶給出一個提示,這種方法實現起來就困難了,還有更為復雜的任務調度時間要求。
二、Quartz
OpenSymphony 的Quartz提供了一個比較完美的任務調度解決方案。
Quartz 是個開源的作業調度框架,為在 Java 應用程序中進行作業調度提供了簡單卻強大的機制。
Quartz中有兩個基本概念:作業和觸發器。作業是能夠調度的可執行任務,觸發器提供了對作業的調度。
1、作業
實現 org.quartz.job 接口,實現接口方法 public void execute(JobExecutionContext context) throws JobExecutionException,在這個方法實現具體的作業任務。
代碼例子:
java 代碼
execute 方法接受一個 JobExecutionContext 對象作為參數。這個對象提供了作業實例的運行時上下文。它提供了對調度器和觸發器的訪問,這兩者協作來啟動作業以及作業的 JobDetail 對象的執行。Quartz 通過把作業的狀態放在 JobDetail 對象中并讓 JobDetail 構造函數啟動一個作業的實例,分離了作業的執行和作業周圍的狀態。JobDetail 對象儲存作業的偵聽器、群組、數據映射、描述以及作業的其他屬性。
2、觸發器
觸發器可以實現對任務執行的調度。Quartz 提供了幾種不同的觸發器,復雜程度各不相同。
簡單觸發器:
public void task() throws SchedulerException {
// Initiate a Schedule Factory
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
// Retrieve a scheduler from schedule factory
Scheduler scheduler = schedulerFactory.getScheduler();
// current time
long ctime = System.currentTimeMillis();
// Initiate JobDetail with job name, job group, and executable job class
JobDetail jobDetail =
new JobDetail("jobDetail-s1", "jobDetailGroup-s1", SimpleQuartzJob.class);
// Initiate SimpleTrigger with its name and group name
SimpleTrigger simpleTrigger =
new SimpleTrigger("simpleTrigger", "triggerGroup-s1");
// set its start up time
simpleTrigger.setStartTime(new Date(ctime));
// set the interval, how often the job should run (10 seconds here)
simpleTrigger.setRepeatInterval(10000);
// set the number of execution of this job, set to 10 times.
// It will run 10 time and exhaust.
simpleTrigger.setRepeatCount(100);
// set the ending time of this job.
// We set it for 60 seconds from its startup time here
// Even if we set its repeat count to 10,
// this will stop its process after 6 repeats as it gets it endtime by then.
// simpleTrigger.setEndTime(new Date(ctime + 60000L));
// set priority of trigger. If not set, the default is 5
// simpleTrigger.setPriority(10);
// schedule a job with JobDetail and Trigger
scheduler.scheduleJob(jobDetail, simpleTrigger);
// start the scheduler
scheduler.start();
}
首先實例化一個 SchedulerFactory,獲得調度器。創建 JobDetail 對象時,它的構造函數要接受一個 Job 作為參數。SimpleTrigger 是一個簡單的觸發器。在創建對象之后,設置幾個基本屬性以立即調度任務,然后每 10 秒重復一次,直到作業被執行 100 次。
Cron觸發器
CronTrigger 支持比 SimpleTrigger 更具體強大的調度,實現起來卻不是很復雜。CronTrigger基于 cron 表達式,支持類似日歷的重復間隔更為復雜的調度時間上的要求。
Cron 表達式包括以下 7 個字段:
·秒
·分
·小時
·月內日期
·月
·周內日期
·年(可選字段)
Cron 觸發器利用一系列特殊字符,如下所示:
·反斜線(/)字符表示增量值。例如,在秒字段中“5/15”代表從第 5 秒開始,每 15 秒一次。
·問號(?)字符和字母 L 字符只有在月內日期和周內日期字段中可用。問號表示這個字段不包含具體值。所以,如果指定月內日期,可以在周內日期字段中插入“?”,表示周內日期值無關緊要。字母 L 字符是 last 的縮寫。放在月內日期字段中,表示安排在當月最后一天執行。在周內日期字段中,如果“L”單獨存在,就等于“7”,否則代表當月內周內日期的最后一個實例。所以“0L”表示安排在當月的最后一個星期日執行。
·在月內日期字段中的字母(W)字符把執行安排在最靠近指定值的工作日。把“1W”放在月內日期字段中,表示把執行安排在當月的第一個工作日內。
·井號(#)字符為給定月份指定具體的工作日實例。把“MON#2”放在周內日期字段中,表示把任務安排在當月的第二個星期一。
·星號(*)字符是通配字符,表示該字段可以接受任何可能的值。
所有這些定義看起來可能有些嚇人,但是只要幾分鐘練習之后,cron 表達式就會顯得十分簡單。
下面的代碼顯示了 CronTrigger 的一個示例。請注意 SchedulerFactory、Scheduler 和 JobDetail 的實例化,與 SimpleTrigger 示例中的實例化是相同的。在這個示例中,只是修改了觸發器。這里指定的 cron 表達式(“0/5 * * * * ?”)安排任務每 5 秒執行一次。
public void task() throws SchedulerException {
// Initiate a Schedule Factory
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
// Retrieve a scheduler from schedule factory
Scheduler scheduler = schedulerFactory.getScheduler();
// current time
long ctime = System.currentTimeMillis();
// Initiate JobDetail with job name, job group, and executable job class
JobDetail jobDetail =
new JobDetail("jobDetail2", "jobDetailGroup2", SimpleQuartzJob.class);
// Initiate CronTrigger with its name and group name
CronTrigger cronTrigger = new CronTrigger("cronTrigger", "triggerGroup2");
try {
// setup CronExpression
CronExpression cexp = new CronExpression("0/5 * * * * ?");
// Assign the CronExpression to CronTrigger
cronTrigger.setCronExpression(cexp);
} catch (Exception e) {
e.printStackTrace();
}
// schedule a job with JobDetail and Trigger
scheduler.scheduleJob(jobDetail, cronTrigger);
// start the scheduler
scheduler.start();
}
三、Spring + Quartz
spring對Java的Timer類和Quartz都提供了一個抽象層,使用我們可以更方便地使用它們。
1、spring與Timer的集成
首先是一個定時器任務
public class EmailReportTask extends TimerTask {
public EmailReportTask() {
}
public void run() {
courseService.sendCourseEnrollmentReport();
}
private CourseService courseService;
public void setCourseService(CourseService courseService) {
this.courseService = courseService;
}
}
spring配置文件中配置EmailReportTask
1.<bean id="reportTimerTask"
2. class="com.springinaction.training.schedule.EmailReportTask">
3. <property name="courseService">
4. <ref bean="courseService"/>
5. </property>
6.</bean>
調度定時器任務
xml 代碼
1.<bean id="scheduledReportTask"
2.
3. class="org.springframework.scheduling.timer.ScheduledTimerTask">
4.
5.
6. <property name="timerTask">
7.
8. <ref bean="reportTimerTask"/>
9.
10. property>
11.
12. <property name="period">
13.
14. <value>86400000<value>
15.
16.
17. property>
18.
19. <property name="delay">
20.
21. <value>32000value>
22.
23. property>
24.
25.bean>
26.
啟動定時器
xml 代碼
1.<bean class="org.springframework.scheduling.timer.TimerFactoryBean">
2. <property name="scheduledTimerTasks">
3. <list>
4. <ref bean="scheduledReportTask"/>
5. list>
6. property>
7.bean>
2、spring與Quartz的集成
創建一個工作
1.public class EmailReportJob extends QuartzJobBean {
2.
3. public EmailReportJob() {
4.
5. }
6.
7. protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
8. courseService.sendCourseEnrollmentReport();
9. }
10.
11. private CourseService courseService;
12. public void setCourseService(CourseService courseService) {
13. this.courseService = courseService;
14. }
15.}
在spring配置文件的配置EmailReportJob
1.<bean id="reportJob"
2. class="org.springframework.scheduling.quartz.JobDetailBean">
3. <property name="jobClass">
4. <value>com.springinaction.training.schedule.EmailReportJobvalue>
5. property>
6. <property name="jobDataAsMap">
7. <map>
8. <entry key="courseService">
9. <ref bean="courseService"/>
10. entry>
11. map>
12. property>
13.bean>
調度工作
和quartz對應,Spirng提供了兩個觸發器SimpleTriggerBean和CronTriggerBean
使用SimpleTriggerBean觸發器
1.<bean id="simpleReportTrigger"
2. class="org.springframework.scheduling.quartz.SimpleTriggerBean">
3. <property name="jobDetail">
4. <ref bean="reportJob"/>
5. property>
6. <property name="startDelay">
7. <value>3600000value>
8. property>
9. <property name="repeatInterval">
10. <value>86400000value>
11. property>
12.bean>
使用CronTriggerBean觸發器
1.<bean id="cronReportTrigger"
2. class="org.springframework.scheduling.quartz.CronTriggerBean">
3. <property name="jobDetail">
4. <ref bean="reportJob"/>
5. property>
6. <property name="cronExpression">
7. <value>0 * * * * ?value>
8. property>
9.bean>
系統會在每分鐘的0秒執行調度任務。