在已有 Spring MVC 專案中整合 Quartz 實現定時任務,是 Java 後端開發的高頻需求。本文記錄從 Maven 依賴、web.xml 掃描、Spring Quartz 配置文件、Job 類實現、Cron 表達式配置到常見坑點(重複執行、懶加載)的完整落地過程。
<!-- 定時器依賴 開始 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<!-- 定時器依賴 結束 -->
關鍵:`spring-context-support` 提供 Spring 對 Quartz 的封裝類(`SchedulerFactoryBean`、`CronTriggerFactoryBean` 等),缺少會報 `NoClassDefFoundError`。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
命名新建配置文件為 `spring-quartz.xml`,自動被 `spring-*.xml` 掃描載入。
package com.tec.kevin.quartz;
public class JobTest {
public void work() {
System.out.println("=== 定時任務執行: " + new java.util.Date() + " ===");
// 業務邏輯:發送郵件、同步數據、生成報表等
}
}
=== 定時任務執行: Tue Jul 25 10:00:00 CST 2023 === === 定時任務執行: Tue Jul 25 10:00:10 CST 2023 === === 定時任務執行: Tue Jul 25 10:00:20 CST 2023 ===
按 Cron 表達式 `0/10 * * * * ?` 每 10 秒準時觸發。
現象:同一時間觸發兩次任務。
原因:`web.xml` 中 `contextConfigLocation` 配置重複,或同時在 `spring-mvc.xml` 和 `spring-quartz.xml` 中掃描了同一包,導致 `SchedulerFactoryBean` 初始化兩次。
解決:確保 `contextConfigLocation` 僅掃描一次,或在 `SchedulerFactoryBean` 設置 `lazy-init="false"` 並檢查無重複 Bean 定義。
| 字段 | 允許值 | 特殊字符 |
|---|---|---|
| 秒 | 0-59 | , - * / |
| 分 | 0-59 | , - * / |
| 時 | 0-23 | , - * / |
| 日 | 1-31 | , - * ? / L W |
| 月 | 1-12 / JAN-DEC | , - * / |
| 周 | 1-7 / SUN-SAT | , - * ? / L # |
常用範例:
`0 0 2 * * ?` 每天凌晨 2 點
`0 0/5 * * * ?` 每 5 分鐘
`0 15 10 ? * MON-FRI` 工作日 10:15
`0 0 12 1 * ?` 每月 1 號中午 12 點
Spring 3.0+ 內建 `@Scheduled` + `TaskScheduler`,輕量級定時任務無需 Quartz:
<task:annotation-driven executor="taskExecutor" scheduler="taskScheduler"/> <task:executor id="taskExecutor" pool-size="5"/> <task:scheduler id="taskScheduler" pool-size="10"/>
@Component
public class SimpleJob {
@Scheduled(cron = "0/10 * * * * ?")
public void work() {
System.out.println("Spring 內建定時: " + new Date());
}
}
適用於單機、簡單定時;集群、持久化、動態管理需上 Quartz。
Spring + Quartz 整合核心步驟:
1. 引入 `spring-context-support` + `quartz` 依賴。
2. `web.xml` 掃描 `spring-*.xml`。
3. 配置 `SchedulerFactoryBean` + `CronTriggerFactoryBean` + `MethodInvokingJobDetailFactoryBean` + 目標 Bean。
4. 實現無參 `public void work()` 方法。
5. 注意 `lazy-init="false"` 確保啟動即調度,避免重複配置導致雙重執行。
掌握此模式,可快速為任何 Spring 專案添加健壯的定時任務能力。