專案初期,首頁商品資料僅在專案啟動時由 ServletContextListener 載入至 Application 域。當後台新增商品後,必須重啟伺服器才能同步,這顯然不符合電商網站「首頁資料自動更新」的需求。
引入 java.util.Timer 與 TimerTask,建立一個守護執行緒,定期(如每小時)從資料庫抓取最新熱門分類與推薦商品,更新 Application 域中的 bigList,前台頁面透過 JSTL 讀取 Application 資料,即可呈現最新商品,無需重啟。
Timer(boolean isDaemon) 可設為守護執行緒(主執行緒結束時自動終止)。Runnable 的抽象類別,繼承並覆寫 run() 即可定義具體任務邏輯。@Component
public class ProductTimerTask extends TimerTask {
@Resource
private ProductService productService;
@Resource
private CategoryService categoryService;
private ServletContext application;
// 由 Listener 注入 ServletContext
public void setApplication(ServletContext application) {
this.application = application;
}
@Override
public void run() {
System.out.println("---- 定時同步首頁資料 ----");
List<List<Product>> bigList = new ArrayList<>();
// 1. 查詢熱門分類
for (Category category : categoryService.queryByHot(true)) {
// 2. 依分類 ID 取得推薦商品
List<Product> list = productService.queryByCategoryId(category.getId());
bigList.add(list);
}
// 3. 更新 Application 域
if (application != null) {
application.setAttribute("bigList", bigList);
}
}
}
public class InitDataListener implements ServletContextListener {
private ProductTimerTask productTimerTask;
private ApplicationContext context;
@Override
public void contextInitialized(ServletContextEvent event) {
// 1. 從 Spring 容器取得 Task Bean
context = WebApplicationContextUtils.getWebApplicationContext(event.getServletContext());
productTimerTask = (ProductTimerTask) context.getBean("productTimerTask");
// 2. 注入 ServletContext 供 Task 寫入 Application
productTimerTask.setApplication(event.getServletContext());
// 3. 啟動定時器:守護執行緒、立即執行、每小時 (1000*60*60 ms) 執行一次
new Timer(true).schedule(productTimerTask, 0, 1000 * 60 * 60);
}
@Override
public void contextDestroyed(ServletContextEvent event) {
// 容器關閉時的清理工作(若有)
}
}
<listener>
<listener-class>com.shop.listener.InitDataListener</listener-class>
</listener>
run() 內建議加上 try-catch 並記錄日誌,避免單次異常導致後續排程停止。透過 Timer + TimerTask 搭配 ServletContextListener,輕鬆實現首頁資料的定時自動同步,解決「後台異動、前台不重啟不更新」的痛點,架構簡單且符合輕量級專案需求。