Quartz 是功能強大的開源任務調度庫,Spring 通過 spring-context-support 模塊提供無縫整合。核心組件:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.3.x</version>
</dependency>
<!-- Quartz 已包含在 spring-context-support 中,無需額外引入 -->
<!-- 解決 commons-collections 版本衝突 -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<web-app xmlns="https://xmlns.jcp.org/xml/ns/javaee" version="3.1">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 1. 任務實現 Bean -->
<bean id="reportTask" class="com.job.ReportGenerationTask"/>
<!-- 2. 方法調用封裝 (MethodInvokingJobDetailFactoryBean) -->
<bean id="reportJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="reportTask"/>
<property name="targetMethod" value="generateDailyReport"/>
<property name="concurrent" value="false"/> <!-- 禁止並發執行 -->
</bean>
<!-- 3. Cron 觸發器 -->
<bean id="reportTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="reportJobDetail"/>
<!-- 每天凌晨 2:30 執行 -->
<property name="cronExpression" value="0 30 2 * * ?"/>
</bean>
<!-- 4. 簡單觸發器 (固定頻率) -->
<bean id="heartbeatTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="heartbeatJobDetail"/>
<property name="repeatInterval" value="30000"/> <!-- 30 秒 -->
<property name="startDelay" value="10000"/> <!-- 10 秒後首次執行 -->
</bean>
<!-- 5. 調度器工廠 -->
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="reportTrigger"/>
<ref bean="heartbeatTrigger"/>
</list>
</property>
<property name="overwriteExistingJobs" value="true"/>
<property name="startupDelay" value="30"/> <!-- 啟動 30 秒後啟動調度 -->
<property name="autoStartup" value="true"/>
<!-- 數據源配置 (集群模式必需) -->
<!-- <property name="dataSource" ref="dataSource"/> -->
<!-- <property name="transactionManager" ref="transactionManager"/> -->
</bean>
</beans>
package com.job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ReportGenerationTask extends QuartzJobBean {
private static final Logger log = LoggerFactory.getLogger(ReportGenerationTask.class);
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
// 可從 context 獲取傳遞參數
String param = context.getMergedJobDataMap().getString("customParam");
log.info("執行報表生成任務,參數: {}", param);
generateDailyReport();
}
public void generateDailyReport() {
// 業務邏輯:查詢數據、生成報表、發送郵件、存儲文件
log.info("每日報表生成完成");
}
}
package com.job;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OrderCleanupTask {
private static final Logger log = LoggerFactory.getLogger(OrderCleanupTask.class);
public void cleanupExpiredOrders() {
log.info("開始清理過期訂單...");
// 業務邏輯
log.info("過期訂單清理完成");
}
}
格式:秒 分 時 日 月 周 [年]
字段 允許值 特殊字符
秒 0-59 , - * /
分 0-59 , - * /
時 0-23 , - * /
日 1-31 , - * ? / L W C
月 1-12 / JAN-DEC , - * /
周 1-7 / SUN-SAT , - * ? / L C #
年(可選) 1970-2099 , - * /
特殊字符含義:
* : 所有值
? : 無具體值 (僅日/周字段互斥)
- : 範圍 (如 10-12 時 表示 10,11,12 點)
, : 枚舉值 (如 MON,WED,FRI)
/ : 增量 (如 0/15 分 表示每 15 分鐘)
L : 最後 (日字段=月末,周字段=週六/最後一個周五)
W : 工作日 (最近的工作日,如 15W 表示該月 15 號最近的工作日)
# : 第 N 個 (如 6#3 表示該月第三個週五)
C : 日曆關聯 (需關聯 Calendar)
| 表達式 | 含義 |
|---|---|
0 0 12 * * ? | 每天中午 12:00 |
0 15 10 ? * * | 每天上午 10:15 |
0 0/5 14 * * ? | 每天 14:00-14:55 每 5 分鐘 |
0 0/5 14,18 * * ? | 每天 14:00-14:55 和 18:00-18:55 每 5 分鐘 |
0 0-5 14 * * ? | 每天 14:00-14:05 每分鐘 |
0 10,44 14 ? 3 WED | 每年 3 月週三 14:10 和 14:44 |
0 15 10 ? * MON-FRI | 週一至週五 10:15 |
0 15 10 15 * ? | 每月 15 號 10:15 |
0 15 10 L * ? | 每月最後一天 10:15 |
0 15 10 ? * 6L | 每月最後一個週五 10:15 |
0 15 10 ? * 6#3 | 每月第三個週五 10:15 |
<!-- quartz.properties 或在 SchedulerFactoryBean 中配置 -->
org.quartz.scheduler.instanceName = MyClusteredScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000
org.quartz.jobStore.useProperties = false
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
<bean id="schedulerFactory" 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.isClustered">true</prop>
<prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop>
</props>
</property>
<property name="configLocation" value="classpath:quartz.properties"/>
</bean>
| 問題 | 原因 | 解決 |
|---|---|---|
| 任務不執行 | Scheduler 未啟動、Trigger 配置錯誤、Bean 未注入 | 檢查 autoStartup=true、日誌輸出、@Autowired 注入 |
| 並發執行導致數據錯亂 | concurrent=false 未生效、集群節點時間不同步 | 設置 @DisallowConcurrentExecution、NTP 同步時間 |
| 任務執行超時/卡死 | 業務邏輯死循環、資源未釋放、線程池耗盡 | 設置執行超時、監控線程堆棧、優化業務邏輯 |
| 併發修改異常 | 集群模式下無樂觀鎖、版本衝突 | 確保數據庫表有版本字段、使用 RowVersion |
| commons-collections 版本衝突 | Hibernate 依賴舊版本 | 排除舊版,強制使用 3.2.2+ |
@Configuration
@EnableScheduling
public class QuartzConfig {
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource,
PlatformTransactionManager txManager) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(txManager);
factory.setOverwriteExistingJobs(true);
factory.setAutoStartup(true);
factory.setStartupDelay(30);
return factory;
}
@Bean
public JobDetailFactoryBean reportJobDetail() {
JobDetailFactoryBean factory = new JobDetailFactoryBean();
factory.setJobClass(ReportGenerationTask.class);
factory.setDurability(true);
return factory;
}
@Bean
public CronTriggerFactoryBean reportTrigger(JobDetail reportJobDetail) {
CronTriggerFactoryBean factory = new CronTriggerFactoryBean();
factory.setJobDetail(reportJobDetail);
factory.setCronExpression("0 30 2 * * ?");
return factory;
}
}
Spring 整合 Quartz 提供了企業級任務調度能力,支持集群、持久化、動態管理、事務邊界控制。生產環境建議採用集群模式 + 數據庫持久化 + 監控告警,確保定時任務高可用、可追蹤、可治理。