在 Java Web 應用中,定時自動執行任務(如定時備份、報表生成、資料同步)是常見需求。本文通過一個完整的檔案上傳範例,詳細講解如何利用 TimerTask 和 ServletContextListener 實現可靠的定時任務機制。
整個方案包含四個核心組件:
TimerTask,定義執行條件與頻率ServletContextListener,管理任務生命週期package com.sun.time2;
public class UploadFile {
/**
* 實現上傳的具體邏輯
* 參考:smarupload 實現、Struts2 多檔案上傳
*/
public void upload() {
System.out.println("系統自動執行檔案上傳任務");
// 實際業務邏輯:連接 FTP、雲存儲、資料庫等
}
}
package com.sun.time2;
import java.util.Calendar;
import java.util.TimerTask;
import javax.servlet.ServletContext;
public class MyTask extends TimerTask {
private static boolean isRunning = false;
private final ServletContext context;
private final Calendar calendar = Calendar.getInstance();
public MyTask(ServletContext context) {
this.context = context;
}
@Override
public void run() {
// 防止並發執行
if (!isRunning) {
// 每小時第 19 分鐘執行(可改為 Cron 表達式邏輯)
if (19 == calendar.get(Calendar.MINUTE)) {
isRunning = true;
context.log("正在執行排程任務");
try {
new UploadFile().upload();
context.log("任務順利完成");
} catch (Exception e) {
context.log("任務執行失敗: " + e.getMessage());
} finally {
isRunning = false;
}
}
} else {
System.out.println("上一任務尚未執行完畢,跳過本次");
}
}
}
package com.sun.time2;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.Timer;
public class ContextListener implements ServletContextListener {
private Timer timer = null;
@Override
public void contextInitialized(ServletContextEvent event) {
timer = new Timer(true); // 守護線程,不阻塞 JVM 關閉
event.getServletContext().log("伺服器已啟動,初始化排程任務");
// 每 90 秒掃描一次(生產環境建議調整為 60 秒或更長)
timer.schedule(new MyTask(event.getServletContext()), 0, 90_000);
event.getServletContext().log("排程任務列表已載入");
}
@Override
public void contextDestroyed(ServletContextEvent event) {
if (timer != null) {
timer.cancel();
event.getServletContext().log("排程任務已銷毀");
}
}
}
<listener>
<listener-class>com.sun.time2.ContextListener</listener-class>
</listener>
上述方案雖然可行,但存在硬編碼時間、單機故障無法切換、無法動態調整等問題。現代專案建議:
@EnableScheduling + @Scheduled(cron = "0 19 * * * ?")| 問題 | 原方案 | 改進方案 |
|---|---|---|
| 時間硬編碼 | Calendar.MINUTE == 19 | 外部化配置 + Cron 表達式 |
| 併發控制 | boolean 旗標 | 分散式鎖 |
| 單點故障 | 無 | 叢集模式 + 領導者選舉 |
| 監控告警 | System.out/log | Metrics + 告警系統 |
基於 TimerTask + ServletContextListener 的定時任務實現簡單直接,適合輕量級單機應用。對於企業級專案,強烈建議採用成熟的調度框架(Quartz、Spring Scheduling、XXL-JOB),以獲得更好的可靠性、可觀測性和運維體驗。