Spring 提供兩種主流定時任務實作方式:內建的 @Scheduled 註解與整合 Quartz 排程器。本文詳細比較兩者特性、配置與適用場景。
@Configuration
@EnableScheduling // 關鍵:啟用排程支援
public class SchedulerConfig { }
@Component
public class SimpleScheduledTasks {
private static final Logger log = LoggerFactory.getLogger(SimpleScheduledTasks.class);
// 固定延遲:上次執行結束後 5 秒再執行
@Scheduled(fixedDelay = 5000)
public void fixedDelayTask() {
log.info("fixedDelay 執行時間: {}", LocalTime.now());
}
// 固定頻率:每 5 秒執行一次 (無視執行耗時)
@Scheduled(fixedRate = 5000)
public void fixedRateTask() {
log.info("fixedRate 執行時間: {}", LocalTime.now());
}
// 初始延遲 2 秒,之後固定頻率
@Scheduled(initialDelay = 2000, fixedRate = 5000)
public void initialDelayTask() { /* ... */ }
// Cron 運算式:每天凌晨 2:30 執行
@Scheduled(cron = "0 30 2 * * ?")
public void cronTask() {
log.info("Cron 任務執行: {}", LocalDateTime.now());
}
}
秒 分 時 日 月 周 [年] * * * * * ? 每秒執行 0 * * * * ? 每分鐘執行 0 0 * * * ? 每小時執行 0 0 2 * * ? 每天凌晨 2 點 0 30 2 * * ? 每天 2:30 0 0 2 ? * MON 每週一凌晨 2 點 0 0 2 1 * ? 每月 1 號凌晨 2 點
@Scheduled(
cron = "0 0/5 * * * ?", // Cron 運算式
zone = "Asia/Taipei", // 時區 (預設系統時區)
fixedDelay = 5000, // 固定延遲 (ms)
fixedRate = 5000, // 固定頻率 (ms)
initialDelay = 1000 // 初始延遲 (ms)
)
public void advancedTask() { /* ... */ }
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
| 組件 | 說明 |
|---|---|
| Job | 實作 org.quartz.Job 介面,定義任務邏輯 |
| JobDetail | 任務描述:名稱、群組、持久化資料 |
| Trigger | 觸發器:SimpleTrigger (簡單)、CronTrigger (Cron) |
| Scheduler | 排程器:管理 Job 與 Trigger 的註冊、啟動、暫停 |
// 1. 定義 Job
@Component
public class QuartzJob implements Job {
private static final Logger log = LoggerFactory.getLogger(QuartzJob.class);
@Override
public void execute(JobExecutionContext context) {
JobDataMap data = context.getMergedJobDataMap();
String param = data.getString("customParam");
log.info("Quartz 任務執行: param={}, time={}", param, LocalDateTime.now());
}
}
// 2. 配置排程
@Configuration
public class QuartzConfig {
@Autowired private SchedulerFactoryBean schedulerFactory;
@PostConstruct
public void init() throws SchedulerException {
Scheduler scheduler = schedulerFactory.getScheduler();
// 定義 JobDetail
JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class)
.withIdentity("myJob", "group1")
.usingJobData("customParam", "Hello Quartz")
.storeDurably()
.build();
// 定義 Cron Trigger
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 * * * ?")) // 每 2 分鐘
.build();
// 註冊並啟動
scheduler.scheduleJob(jobDetail, trigger);
if (!scheduler.isStarted()) scheduler.start();
}
}
@RestController
@RequiredArgsConstructor
public class QuartzController {
private final Scheduler scheduler;
@PostMapping("/jobs/{name}/pause")
public void pauseJob(@PathVariable String name) throws SchedulerException {
scheduler.pauseJob(JobKey.jobKey(name, "group1"));
}
@PostMapping("/jobs/{name}/resume")
public void resumeJob(@PathVariable String name) throws SchedulerException {
scheduler.resumeJob(JobKey.jobKey(name, "group1"));
}
@DeleteMapping("/jobs/{name}")
public void deleteJob(@PathVariable String name) throws SchedulerException {
scheduler.deleteJob(JobKey.jobKey(name, "group1"));
}
@PutMapping("/jobs/{name}/reschedule")
public void rescheduleJob(@PathVariable String name, @RequestParam String cron)
throws SchedulerException {
TriggerKey triggerKey = TriggerKey.triggerKey(name, "group1");
CronTrigger newTrigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
scheduler.rescheduleJob(triggerKey, newTrigger);
}
}
| 比較項目 | @Scheduled | Quartz |
|---|---|---|
| 複雜度 | 低 (註解驅動) | 中高 (需配置 Job/Trigger) |
| 動態管理 | 不支援 (需重啟修改) | 完整支援 (Runtime 新增/修改/刪除) |
| 叢集支援 | 不支援 | 原生支援 (資料庫鎖機制) |
| 持久化 | 無 | JDBC JobStore 完整支援 |
| Misfire 處理 | 無 | 豐富策略 (忽略、立即執行、智能策略) |
| 監控管理 | 僅靠日誌 | 可整合 Quartz Manager/UI |
| 適用場景 | 簡單固定任務、單機應用 | 企業級、叢集、動態排程、需持久化 |
Spring 定時任務從簡單的 @Scheduled 到強大的 Quartz 整合,滿足不同層級的排程需求。理解兩者差異與適用場景,能為專案選擇最合適的技術方案,避免過度設計或功能不足。