在 Spring Batch、Quartz 等重型調度框架普及前,利用 Servlet 規範提供的 ServletContextListener 配合 JDK java.util.Timer 即可輕鬆實現 Web 應用啟動/停止時自動啟動/關閉定時任務,零依賴、輕量級。
contextInitialized(ServletContextEvent):容器啟動時調用,啟動定時器。contextDestroyed(ServletContextEvent):容器關閉時調用,優雅關閉定時器。package com.hanweb.jcms;
import javax.servlet.*;
public class ReplyListener implements ServletContextListener {
private ReplyTimer replyTimer;
@Override
public void contextInitialized(ServletContextEvent event) {
String msg = "[SYS] SMS reply listener start.";
event.getServletContext().log(msg);
System.out.println(msg);
replyTimer = new ReplyTimer(1); // 每 1 分鐘
replyTimer.start();
}
@Override
public void contextDestroyed(ServletContextEvent event) {
String msg = "[SYS] SMS reply listener stop.";
event.getServletContext().log(msg);
System.out.println(msg);
if (replyTimer != null) replyTimer.stop();
}
}
package com.hanweb.jcms;
import java.util.*;
public class ReplyTimer {
private final Timer timer = new Timer();
private final int intervalMinutes;
public ReplyTimer(int minutes) { this.intervalMinutes = minutes; }
public void start() {
Date firstRun = new Date(); // 立即執行
long period = intervalMinutes * 60 * 1000L;
timer.schedule(new ReplyTask(), firstRun, period);
}
public void stop() { timer.cancel(); }
}
package com.hanweb.jcms;
import java.util.*;
public class ReplyTask extends TimerTask {
@Override
public void run() { doSomething(); }
private void doSomething() {
System.out.println("[SYS] SMS reply listener running at " + new Date());
// 實際業務:輪詢短信網關、處理回覆、更新數據庫等
}
}
<web-app>
<listener>
<listener-class>com.hanweb.jcms.ReplyListener</listener-class>
</listener>
</web-app>
編譯後將類文件置於 WEB-INF/classes,重啟應用即可生效。
| 優點 | 缺點 |
|---|---|
| 零第三方依賴,JDK 內置 | 無持久化,重啟丟失排程狀態 |
| 代碼極簡,易理解維護 | 無分佈式協調,集群下重複執行 |
| 啟動/停止與 Web 容器生命周期綁定 | 異常處理簡陋,任務失敗無重試機制 |
| 適合輕量級周期性業務 (同步、清理、報表) | 精度受限於 Timer (毫秒級抖動) |
生產環境建議遷移至 Spring @Scheduled (單機) 或 Quartz/Elastic-Job/XXL-JOB (分佈式),獲得持久化、集群分片、失敗重試、監控儀表板等企業級能力。本方案適用於原型驗證、輕量內部工具或受限環境無法引入新依賴的場景。