使用 Quartz


JDK的標準API中所提供的Timer功能有限,只能指定任務與任務之間的期間(Period),無法指定某個時間點定時執行任務,您可以使用Quartz,它提供了更多的排程功能,而Spring則對Quartz進行了封裝,讓它在使用上更加方便。

您可以繼承org.springframework.scheduling.quartz.QuartzJobBean來實作一個Job類別,例如:
  • DemoJob.java
package onlyfun.caterpillar;

import org.quartz.JobExecutionContext;
import org.springframework.scheduling.
quartz.QuartzJobBean;

public class DemoJob extends QuartzJobBean {
private JobData jobData;

public void executeInternal(
JobExecutionContext context) {
System.out.println(
jobData.getData() + " is executed.");
}

public void setJobData(JobData jobData) {
this.jobData = jobData;
}

public JobData getJobData() {
return jobData;
}
}

JobData只是一個Job資料物件的示範類別,為了能看出排程Job被執行時的週期性,它會傳回一個Date物件表示執行Job時所需資料的傳回時間,例如:
  • JobData.java
package onlyfun.caterpillar;

import java.util.Date;

public class JobData {
public String getData() {
return "Data from "
+ new Date().toString();
}
}

直接來看定義檔如何定義:
  • beans-config.xml
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
<bean id="someData"
class="onlyfun.caterpillar.JobData"/>

<bean id="jobDetailBean"
class="org.springframework.scheduling.
→ quartz.JobDetailBean">
<property name="jobClass">
<value>onlyfun.caterpillar.DemoJob</value>
</property>
<property name="jobDataAsMap">
<map>
<entry key="jobData">
<ref bean="someData"/>
</entry>
</map>
</property>
</bean>

<bean id="simpleTriggerBean"
class="org.springframework.scheduling.
→ quartz.SimpleTriggerBean">
<property name="jobDetail">
<ref bean="jobDetailBean"/>
</property>
<property name="repeatInterval">
<value>1000</value>
</property>
<property name="startDelay">
<value>1000</value>
</property>
</bean>

<bean id="schedulerFactoryBean"
class="org.springframework.scheduling.
→ quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="simpleTriggerBean"/>
</list>
</property>
</bean>
</beans>
在以上設定中比較特別要注意的是, org.springframework.scheduling.quartz.JobDetailBean的"jobClass"屬性必須提供Job的 類別名稱,而不是Job的Bean實例,而Job所需的資料可以在"jobDataAsMap"屬性中來提供。

在排程任務的週期指定上,使用org.springframework.scheduling.quartz.SimpleTriggerBean來指 定,這點與 TimerTask 排程中的指定方式類似,指定的時間同樣也是以毫秒作為單位,而排定Job時,所使用的是 org.springframework.scheduling.quartz.SchedulerFactoryBean。

完成設定之後,只要啟動Spring並讀取定義檔完成後,排程任務就會進行,例如撰寫一個簡單的任務啟動類別:
  • QuartzDemo.java
package onlyfun.caterpillar;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.springframework.context.
support.FileSystemXmlApplicationContext;

public class QuartzDemo {
public static void main(String[] args) throws IOException {
new FileSystemXmlApplicationContext("beans-config.xml");
System.out.println("啟動 Task..");
System.out.println("請輸入 exit 關閉 Task: ");

BufferedReader reader =
new BufferedReader(
new InputStreamReader(System.in));

while(true) {
if(reader.readLine().equals("exit")) {
System.exit(0);
}
}
}
}

使用SimpleTriggerBean只能作簡單的Job與Job之間執行的期間(Period)指定,如果要直接作時間點的指定,則可以使用org.springframework.scheduling.quartz.CronTriggerBean,例如:
  • beans-config.xml
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
<bean id="someData"
class="onlyfun.caterpillar.JobData"/>

<bean id="jobDetailBean"
class="org.springframework.scheduling.
→ quartz.JobDetailBean">
<property name="jobClass">
<value>onlyfun.caterpillar.DemoJob</value>
</property>
<property name="jobDataAsMap">
<map>
<entry key="jobData">
<ref bean="someData"/>
</entry>
</map>
</property>
</bean>

<bean id="cronTriggerBean"
class="org.springframework.scheduling.
→ quartz.CronTriggerBean">
<property name="jobDetail">
<ref bean="jobDetailBean"/>
</property>
<property name="cronExpression">
<value>0 0 19 * * ?</value>
</property>
</bean>

<bean id="schedulerFactoryBean"
class="org.springframework.scheduling.
→ quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTriggerBean"/>
</list>
</property>
</bean>
</beans>

重點在於"cronExpression"屬性的指定,指定的格式是至少六個時間元素,最多七個時間元素,例如上面的指定是每天的19時要執行Job一次,"cronExpression"屬性指定的格式如下:
  • 秒(0-59)
  • 分(0-59)
  • 小時(0-23)
  • 每月第幾天(1-31)
  • 月(1-12或JAN-DEC)
  • 每星期第幾天(1-7或SUN-SAT)
  • 年(1970-2099)

其中「每月第幾天」與「每星期第幾天是互斥」的,兩個只能設定一個,不設定的以 ? 符號撰寫,如果有好幾個時間點,可以使用 , 符號,例如:「0 0 10,12,14 * * ?」表示每天的10時、12時、14時要執行Job;對於連續的時間可以使用 - 符號,例如「0 0 10,12,14 1-15 * ?」表示每月的1到15日每10時、12時、15時要執行Job,時間格式中的年指定可有可無,例如:「0 0 10,12,14 ? * MON 2006」表示2006年每星期一的10時、12時、14時要執行Job。