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。