在 Web 應用中,許多耗時操作(報表生成、郵件發送、數據同步)不應阻塞用戶請求。異步線程雖能解決即時響應,但無法應對「每天凌晨 2 點執行」、「每週三下午 3 點跑批」等週期性需求。引入任務調度框架,可將業務邏輯與時間策略解耦,提升系統彈性與用戶體驗。
Quartz 是成熟的開源 Java 調度框架,支援 Cron 表達式、SimpleTrigger 等豐富調度策略。本文示範在 Struts 1.x 環境下,透過 PlugIn 機制於容器啟動時初始化 Scheduler,實現任務的統一管理。
org.apache.struts.action.PlugIn,於 init() 中從 ServletContext 獲取 StdSchedulerFactory,啟動 Scheduler。web.xml 中配置,負責建立 SchedulerFactory 並放入 ServletContext。<plug-in className="SchedulerPlugIn">
<set-property property="startOnLoad" value="true" />
<set-property property="startupDelay" value="5000" />
</plug-in>
startupDelay 確保核心服務(如資料源、緩存)先行就緒。
<servlet>
<servlet-name>QuartzInitializer</servlet-name>
<servlet-class>org.quartz.ee.servlet.QuartzInitializerServlet</servlet-class>
<init-param>
<param-name>shutdown-on-unload</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>start-scheduler-on-load</param-name>
<param-value>false</param-value> <!-- 由 PlugIn 控制啟動 -->
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
所有 Job 需繼承 org.quartz.Job 並實作 execute(JobExecutionContext):
public class ReportJob extends Job {
public void execute(JobExecutionContext context) {
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String reportType = dataMap.getString("reportType");
// 業務邏輯:生成報表、發送郵件...
}
}
調度註冊(可在 Action、Service 或 Listener 中):
// 每天凌晨 2:22 執行
CronTrigger trigger = new CronTrigger("dailyReportTrigger", "reportGroup");
trigger.setCronExpression("0 22 2 * * ?");
JobDetail jobDetail = new JobDetail("dailyReportJob", "reportGroup", ReportJob.class);
jobDetail.getJobDataMap().put("reportType", "PDF");
SchedulerPlugIn.scheduleWork(jobDetail, trigger);
| 需求 | 程式碼片段 |
|---|---|
| 每天固定時間 | TriggerUtils.makeDailyTrigger(2, 22) 或 new CronTrigger(...).setCronExpression("0 22 2 * * ?") |
| 每 N 秒執行 | new SimpleTrigger(...).setRepeatInterval(5000L).setRepeatCount(REPEAT_INDEFINITELY) |
| 每週特定日期 | new CronTrigger(...).setCronExpression("0 0 15 ? * WED") (每週三 15:00) |
| 單次延遲執行 | trigger.setStartTime(new Date(System.currentTimeMillis() + delay)) |
生產環境建議配置 JobStoreTX + 資料庫表(qrtz_*),實現:
關鍵 quartz.properties 片段:
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
org.quartz.scheduler.instanceId = AUTO
JobDataMap 傳遞。execute() 拋出 JobExecutionException 可配合 refireImmediately 策略重試。Quartz 憑藉靈活的調度模型、成熟的集群支援與豐富的生態,仍是 Java 任務調度的首選。透過 Struts PlugIn 或 ServletContextListener 整合,可輕鬆在 Web 容器中建立企業級調度平台。隨著 Spring 生態普及,@Scheduled、QuartzJobBean 等更高層抽象進一步降低了使用門檻,但理解 Quartz 核心機制對排查疑難雜症至關重要。