早期 Struts 開發中,Action 內充斥 try/catch,將異常轉為 ActionError 放入請求,再在 JSP 顯示。Struts 1.1 引入聲明式異常處理,在 struts-config.xml 映射異常類型至轉發頁面與錯誤鍵值,實現業務邏輯與錯誤處理分離。
<global-exceptions>)應用級通用異常映射,所有 Action 生效:
<global-exceptions>
<exception
key="expired.InvalidItemsCatalogName"
type="com.shop.exception.InvalidItemsCatalogNameException"
path="/error.jsp"
scope="request"/>
</global-exceptions>
key:對應 ApplicationResources.properties 鍵值,如 expired.InvalidItemsCatalogName=該商品分類已存在,請重新輸入!type:完全限定異常類名 (支援繼承匹配)。path:轉發目標頁面 (相對上下文根)。scope:request 或 session,決定錯誤信息存活範圍。<exception>)針對特定 Action 覆蓋或補充全局配置:
<action-mappings>
<action path="/addItemsCatalogAction"
type="com.shop.action.ItemsCatalogAction"
name="itemsCatalogForm">
<exception
key="expired.InvalidItemsCatalogName"
type="com.shop.exception.InvalidItemsCatalogNameException"
path="/error.jsp"/>
<forward name="success" path="/tiles.layout"/>
</action>
</action-mappings>
優先級:局部 > 全局。同一異常類型在局部重新聲明可指定不同錯誤頁或信息鍵。
public class ItemsCatalogAction extends BaseAction {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception { // execute 拋出 Exception,滿足異常處理器要求
String act = request.getParameter("act");
ItemsCatalogMap map = new ItemsCatalogMap();
ItemsCatalogForm f = (ItemsCatalogForm) form;
if ("create".equals(act)) {
map.validateNameOfCatalog(f); // 內部拋出 InvalidItemsCatalogNameException
map.create(f);
return mapping.findForward("success");
}
return mapping.findForward("error");
}
}
關鍵點:
execute() 聲明 throws Exception (而非舊版 perform() 僅 IOException, ServletException),使容器能捕獲業務異常並交給異常處理器。validateNameOfCatalog() 直接拋出業務異常,無需 try/catch,代碼極度乾淨。默認 org.apache.struts.action.ExceptionHandler 僅設置錯誤信息並轉發。繼承它可實現:日誌記錄、郵件通知、審計追蹤、差異化響應 (AJAX 返回 JSON、頁面返回 HTML)。
ExceptionHandler 並覆寫 execute():public class CustomExceptionHandler extends ExceptionHandler {
private static final Log LOG = LogFactory.getLog(CustomExceptionHandler.class);
@Override
public ActionForward execute(Exception ex, ExceptionConfig cfg,
ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws ServletException {
// 1. 記錄完整堆棧
LOG.error("Business exception caught", ex);
// 2. 區分 AJAX 請求
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
response.setContentType("application/json;charset=UTF-8");
try {
response.getWriter().write("{\"error\":\"" + getMessage(cfg, request) + "\"}");
} catch (IOException e) { /* ignore */ }
return null; // 已直接響應
}
// 3. 普通請求走默認流程 (設置錯誤信息、轉發)
return super.execute(ex, cfg, mapping, form, request, response);
}
}
struts-config.xml 註冊:<global-exceptions>
<exception
handler="com.shop.struts.CustomExceptionHandler"
key="global.error.message"
path="/error.jsp"
type="java.lang.Exception"/> <!-- 捕獲所有未處理異常 -->
</global-exceptions>
| 場景 | 建議配置 |
|---|---|
| 業務校驗失效 (重複、狀態非法) | 自定義業務異常 + 全局/局部映射 + 友好提示鍵值 |
| 系統級異常 (DB 連接失敗、NPE) | 全局捕獲 java.lang.Exception,統一錯誤頁,記錄日誌 |
| AJAX 接口 | 自定義 Handler 返回 JSON,避免返回整張錯誤頁 HTML |
| 國際化 | key 對應多語言資源文件,scope="request" 避免污染 Session |
Struts 2 以攔截器 (exception interceptor) 替代聲明式配置,功能更強 (結果類型、棧追蹤暴露控制)。但 Struts 1 專案仍廣泛存在,掌握其異常機制對維護遷移至關重要。