在傳統 Web 應用中,自行管理 Timer / TimerTask 容易遇到熱部署導致任務重複執行、線程泄漏、異常吞噬等問題。Spring Framework 內置的調度抽象(TimerFactoryBean、ScheduledTimerTask)配合 IoC 容器生命周期,能優雅解決這些痛點,實現定時任務的聲明式配置、熱插拔與統一管理。
java.util.Timer 實例,綁定容器啟動/關閉TimerTask)、延遲時間、周期間隔java.util.TimerTask 或 Spring 的 Runnable 介面<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/schedulingContext-timer.xml</param-value>
</context-param>
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
load-on-startup 確保容器啟動時初始化調度上下文。
<?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">
<!-- 定時器工廠,管理 Timer 實例 -->
<bean id="timer" class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<list>
<ref local="myScheduledTask1"/>
<!-- 可添加更多任務 -->
</list>
</property>
</bean>
<!-- 業務任務 Bean -->
<bean id="myTimerTask" class="com.example.schedule.MyBusinessTask" />
<!-- 調度包裝:延遲 10s 首次執行,之後每 24 小時 (86400000ms) 執行一次 -->
<bean id="myScheduledTask1" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="timerTask"><ref bean="myTimerTask"/></property>
<property name="delay"><value>10000</value></property>
<property name="period"><value>86400000</value></property>
</bean>
</beans>
package com.example.schedule;
import java.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyBusinessTask extends TimerTask {
private static final Logger log = LoggerFactory.getLogger(MyBusinessTask.class);
@Override
public void run() {
try {
log.info("=== 定時任務開始執行 ===");
// 具體業務邏輯:數據同步、報表生成、緩存清理等
doBusinessLogic();
log.info("=== 定時任務執行完成 ===");
} catch (Exception e) {
log.error("定時任務異常", e); // 關鍵:捕獲異常防止 Timer 線程終止
}
}
private void doBusinessLogic() {
// 實際業務代碼
}
}
Timer 機制下,任務拋出未捕獲異常會導致 Timer 線程死亡,後續所有任務停止。務必在 run() 中 try-catch 所有異常並記錄日誌。
Timer.cancel() 清理線程只需修改 XML 中的 scheduledTimerTasks 列表或注入 TimerFactoryBean 動態調用 getScheduledTimerTasks() 管理,無需重啟應用(需容器支持熱加載上下文)。
現代 Spring 版本提供更強大的 @EnableScheduling + @Scheduled(cron="0 2 * * *") 注解方式,支持 Cron 表達式、線程池、異步執行、錯過執行重跑等高級特性,新項目優先選用。
Spring 的 TimerFactoryBean 方案以極低配置成本,將定時任務納入 IoC 容器統一生命周期管理,解決了傳統 Timer 容易出現的線程泄漏、異常吞噬、熱部署重複執行等問題,是 Spring 2.x 時代及維護老項目的經典最佳實踐。