在 Java EE 系統開發中,定時任務是常見需求:例如每天凌晨生成前日報表、每小時匯總統計數據等。雖然可以使用 java.util.Timer 配合 TimerTask 實現,但調度控制不靈活,且代碼量龐大。引入 Quartz 框架無疑是更優選擇,且其與 Spring 整合非常便捷。
Quartz 是功能強大的企業級任務調度框架。它允許開發者靈活定義觸發器的調度時間表,並支持觸發器與任務的關聯映射。此外,Quartz 提供調度運行環境的持久化機制,可保存調度現場,即使系統故障關閉,任務調度數據也不會丟失。Spring 對 Quartz 進行了封裝與簡化,使其使用更加便捷。
構建基於 Spring 的任務調度服務僅需三步:
quartz.jar 至項目 classpath。applicationContext.xml 中配置任務詳情、觸發器及調度工廠,設定運行頻率、啟動延遲等屬性。Quartz 核心在於 Cron 表達式,由 7 個字段組成(秒、分、時、日、月、周、年[可選])。特殊字符賦予靈活控制:
/ 表示增量(如 5/15 代表從第 5 秒起每 15 秒)? 僅用於「日」和「周」字段,表示無具體值(Spring 要求兩字段必有一個為 ?)L 代表「最後」(月末最後一天、周最後一天)W 指定最近工作日# 指定月內第 N 個星期幾(如 MON#2 為當月第二個週一)* 通配符常用範例:
0 0 08 * * ? 每天上午 8 點觸發0 15 10 ? * 6L 2009-2019 2009-2019 年每月最後一個週五上午 10:15 觸發0 15 10 ? * 6#3 每月第三個週五上午 10:15 觸發package demoinfo.spring.quartz;
public interface IPrintInfoService {
public void print();
}
package demoinfo.spring.quartz;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class PrintInfoServiceImpl implements IPrintInfoService {
public void print() {
Calendar now = Calendar.getInstance();
System.out.println("現在是北京時間:" + format(now.getTime()));
}
private String format(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
}
package demoinfo.spring.quartz;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class PrintInfoJob extends QuartzJobBean {
private IPrintInfoService prinfInfoService;
public void setPrinfInfoService(IPrintInfoService prinfInfoService) {
this.prinfInfoService = prinfInfoService;
}
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
this.prinfInfoService.print();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:context="https://www.springframework.org/schema/context"
xmlns:tx="https://www.springframework.org/schema/tx"
xsi:schemaLocation="
https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.5.xsd
https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-2.5.xsd
https://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 業務 Bean -->
<bean id="printInfoService" class="demoinfo.spring.quartz.PrintInfoServiceImpl" />
<!-- JobDetail 定義 -->
<bean id="printInfoJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="demoinfo.spring.quartz.PrintInfoJob" />
<property name="jobDataAsMap">
<map>
<entry key="prinfInfoService" value-ref="printInfoService" />
</map>
</property>
</bean>
<!-- 簡單觸發器:每 6 秒執行一次 -->
<bean id="simplePrintInfoTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="printInfoJob" />
<property name="startDelay" value="6000" />
<property name="repeatInterval" value="6000" />
</bean>
<!-- Cron 觸發器:每 5 秒執行 -->
<bean id="complexPrintInfoTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="printInfoJob" />
<property name="cronExpression" value="0/5 * * * * ?" />
</bean>
<!-- 調度工廠 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="complexPrintInfoTrigger" />
</list>
</property>
</bean>
</beans>
package demoinfo.spring.quartz;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringQuartzDemo {
public static void main(String[] args) {
System.out.println("測試開始......");
new ClassPathXmlApplicationContext("classpath:demoinfo/spring/quartz/applicationContext.xml");
System.out.println("測試結束......");
}
}
運行測試類,控制台將每 5 秒輸出一次當前時間,驗證定時任務正常工作。