Quartz 是 Java 生態最成熟的定時調度框架,Spring 提供了無縫整合支援,將 Job、Trigger、Scheduler 納入 IOC 容器統一管理。本文系統講解 JobDetail 兩種配置方式、SimpleTrigger/CronTrigger 宣告式配置、SchedulerFactoryBean 統一裝配、集群/持久化/事務/監控 進階主題,並給出生產環境檢查清單。
適用於複雜邏輯、需存取 JobExecutionContext、JobDataMap 的場景。
// 任務實現類繼承 QuartzJobBean
public class SimpleJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("執行時間: " + new Date());
JobDataMap dataMap = context.getMergedJobDataMap();
String wish = dataMap.getString("wish");
System.out.println("參數 wish: " + wish);
}
}
// Spring XML 配置
<bean id="simpleJob" class="com.example.job.SimpleJob" />
<bean id="jobDetailFactoryBeanExample" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.example.job.SimpleJob" />
<property name="jobDataAsMap">
<map>
<entry key="wish" value="hello" />
</map>
</property>
<property name="durability" value="true" /> <!-- 無 Trigger 時也不刪除 -->
</bean>
任務類無需繼承任何介面,純 POJO,測試極其方便,生產首選。
public class ExampleJob {
public void execute() {
System.out.println("現在是 " + new SimpleDateFormat("yyyy年MM月dd日 HH時mm分ss秒").format(new Date()));
}
}
// Spring XML 配置
<bean id="exampleJob" class="com.example.job.ExampleJob" />
<bean id="methodInvokingJobDetailFactoryBeanExample" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="exampleJob" />
<property name="targetMethod" value="execute" />
<property name="concurrent" value="false" /> <!-- 禁止併發執行 -->
</bean>
關鍵屬性:concurrent=false 確保同一任務不並行(上次未結束不啟動下次),等同實作 StatefulJob 介面。
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="methodInvokingJobDetailFactoryBeanExample" />
<property name="startDelay" value="5000" /> <!-- 5 秒後首次執行 -->
<property name="repeatInterval" value="3000" /> <!-- 每 3 秒重複 -->
<property name="repeatCount" value="10" /> <!-- 重複 10 次(預設 -1 無限) -->
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="jobDetailFactoryBeanExample" />
<property name="cronExpression" value="0 0/5 * * * ?" /> <!-- 每 5 分鐘 -->
<property name="timeZone" value="Asia/Shanghai" /> <!-- 關鍵:指定時區 -->
</bean>
常用 Cron 表達式:
| 表達式 | 含義 |
|---|---|
0 0 2 * * ? | 每天凌晨 2 點 |
0 0/10 * * * ? | 每 10 分鐘 |
0 0 0 1 * ? | 每月 1 號 0 點 |
0 0 0 ? * MON | 每週一 0 點 |
0 15 10 ? * 6L | 每月最後一個週五 10:15 |
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
<ref bean="simpleTrigger" />
</list>
</property>
<!-- 進階配置 -->
<property name="overwriteExistingJobs" value="true" /> <!-- 覆蓋同名 Job -->
<property name="startupDelay" value="30" /> <!-- 延遲 30 秒啟動,等待資料庫就緒 -->
<property name="autoStartup" value="true" />
<property name="applicationContextSchedulerContextKey" value="applicationContext" /> <!-- Job 中可獲取 Spring 容器 -->
</bean>
<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="overwriteExistingJobs" value="true" />
<property name="quartzProperties">
<props>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
<prop key="org.quartz.jobStore.isClustered">true</prop>
<prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop>
<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
</props>
</property>
</bean>
集群關鍵點:共享資料庫、isClustered=true、instanceId=AUTO、時間同步(NTP)、資料庫鎖機制保證唯一執行。
任務執行需資料庫事務時,配置 transactionManager,Quartz 會在 executeInternal 外層開啟事務,異常自動回滾。
@Autowired private Scheduler scheduler;
// 新增
scheduler.scheduleJob(jobDetail, trigger);
// 修改觸發時間
scheduler.rescheduleJob(triggerKey, newTrigger);
// 暫停/恢復/刪除
scheduler.pauseJob(jobKey);
scheduler.resumeJob(jobKey);
scheduler.deleteJob(jobKey);
org.quartz.plugin.jobHistory 記錄執行歷史quartz_job_executed_total、quartz_job_duration_seconds/actuator/quartz 端點(需依賴 spring-boot-starter-quartz)@Configuration
@EnableScheduling
public class QuartzConfig {
@Bean
public JobDetailFactoryBean jobDetail() {
JobDetailFactoryBean factory = new JobDetailFactoryBean();
factory.setJobClass(MyJob.class);
factory.setDurability(true);
return factory;
}
@Bean
public CronTriggerFactoryBean trigger(JobDetail jobDetail) {
CronTriggerFactoryBean factory = new CronTriggerFactoryBean();
factory.setJobDetail(jobDetail);
factory.setCronExpression("0 0/5 * * * ?");
factory.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
return factory;
}
@Bean
public SchedulerFactoryBean scheduler(Trigger trigger, DataSource dataSource, PlatformTransactionManager txManager) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setTriggers(trigger);
factory.setDataSource(dataSource);
factory.setTransactionManager(txManager);
factory.setOverwriteExistingJobs(true);
factory.setQuartzProperties(quartzProps());
return factory;
}
private Properties quartzProps() {
Properties props = new Properties();
props.setProperty("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
props.setProperty("org.quartz.jobStore.isClustered", "true");
return props;
}
}
| 項目 | 建議 |
|---|---|
| 時區 | 所有 CronTrigger 顯式設定 timeZone,JVM -Duser.timezone=Asia/Shanghai |
| 併發控制 | concurrent=false 或 實作 StatefulJob,避免重疊執行導致資料不一致 |
| 異常處理 | Job 內捕獲異常記錄日誌,拋出 JobExecutionException 讓 Quartz 決定重試/暫停 |
| 冪等性 | 任務邏輯設計為冪等(唯一鍵、狀態機、分散式鎖),防重複執行 |
| 優雅關閉 | scheduler.shutdown(true) 等待執行中任務完成,配合 Spring @PreDestroy |
| 資料庫索引 | QRTZ 表建立適當索引(SCHED_NAME, TRIGGER_GROUP, NEXT_FIRE_TIME) |
| 日誌追蹤 | MDC 注入 jobName、fireInstanceId,關聯業務日誌 |
Quartz + Spring 整合將調度能力內建於 IOC 容器,MethodInvokingJobDetailFactoryBean + CronTriggerFactoryBean + SchedulerFactoryBean 三劍客覆蓋 95% 場景。進階採用 JDBC JobStore 集群 保證高可用,事務管理 保證資料一致性,動態 API 支援運維靈活性。遵循本文最佳實踐與檢查清單,即可構建穩定、可觀測、可運維的企業級定時調度系統。