攔截器是 Struts 2 架構的靈魂,將橫切關注點(日誌、驗證、國際化、檔案上傳、事務等)從 Action 剝離,形成可組合、可復用的處理鏈。本文系統闡述攔截器原理、內建攔截器清單、配置與引用方式,並通過「計時攔截器」與「授權攔截器」兩個完整案例,示範內建使用與自訂開發全流程。
ServletDispatcher 接收請求,解析 struts.xml 確定 Action 映射。InterceptorChain。intercept(ActionInvocation)。return invocation.invoke() 繼續鏈,或直接返回結果碼(如 LOGIN)短路。execute(),結果再反向通過攔截器(可做後處理)。
Struts 2.0.1 struts-default.xml 定義 30+ 攔截器,常用者:
| 名稱 | 類別 | 用途 |
|---|---|---|
| params | ParametersInterceptor | 請求參數注入 Action 屬性 |
| validation | ValidationInterceptor | 執行驗證邏輯 |
| workflow | DefaultWorkflowInterceptor | 驗證失敗導向 INPUT 結果 |
| i18n | I18nInterceptor | 區域設定管理 |
| fileUpload | FileUploadInterceptor | 多部分檔案上傳處理 |
| timer | TimerInterceptor | 記錄 Action 執行耗時 |
| token | TokenInterceptor | 防重複提交 |
| exception | ExceptionMappingInterceptor | 異常對應結果頁 |
| prepare | PrepareInterceptor | 呼叫 prepare() 方法 |
| modelDriven | ModelDrivenInterceptor | 模型驅動支援 |
| checkbox | CheckboxInterceptor | 處理未勾選 checkbox 的值 |
| scope | ScopeInterceptor | 屬性跨請求範圍管理 |
預設攔截器棧 defaultStack 組合了上述大多數攔截器,繼承 struts-default 套件的 Action 自動套用。
package tutorial;
import com.opensymphony.xwork2.ActionSupport;
public class TimerInterceptorAction extends ActionSupport {
@Override public String execute() {
try { Thread.sleep(500); } catch (Exception ignored) {}
return SUCCESS;
}
}
<package name="InterceptorDemo" extends="struts-default">
<action name="Timer" class="tutorial.TimerInterceptorAction">
<interceptor-ref name="timer"/> <!-- 僅引用 timer,覆蓋 defaultStack -->
<result>/Timer.jsp</result>
</action>
</package>
首次請求:Executed action [ //Timer!execute ] took 2859 ms. (含類載入初始化)
再次請求:Executed action [ //Timer!execute ] took 500 ms. (純業務耗時)
適用於效能基準測試與瓶頸定位。
ActionInvocation 取得上下文。AbstractInterceptor 實作 intercept()。// RoleAware.java - Action 實作此介面以接收角色
public interface RoleAware { void setRole(String role); }
// AuthorizationInterceptor.java
public class AuthorizationInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation ai) throws Exception {
Map session = ai.getInvocationContext().getSession();
String role = (String) session.get("ROLE");
if (role != null) {
Object action = ai.getAction();
if (action instanceof RoleAware) {
((RoleAware) action).setRole(role); // 注入角色
}
return ai.invoke(); // 繼續鏈
}
return Action.LOGIN; // 未登入,短路返回 LOGIN 結果碼
}
}
public class AuthorizatedAccess extends ActionSupport implements RoleAware {
private String role;
public void setRole(String role) { this.role = role; }
public String getRole() { return role; }
@Override public String execute() { return SUCCESS; }
}
// Login.java - 將角色存入 Session
public class Login extends ActionSupport implements SessionAware {
private String role; private Map session;
public void setRole(String r) { role = r; }
public void setSession(Map s) { session = s; }
@Override public String execute() {
session.put("ROLE", role);
return SUCCESS; // 透過 chain 結果類型轉發
}
}
<package name="InterceptorDemo" extends="struts-default">
<interceptors>
<interceptor name="auth" class="tutorial.AuthorizationInterceptor"/>
</interceptors>
<action name="Login" class="tutorial.Login">
<result type="chain">AuthorizatedAccess</result> <!-- 鏈式結果 -->
</action>
<action name="AuthorizatedAccess" class="tutorial.AuthorizatedAccess">
<interceptor-ref name="auth"/> <!-- 套用自訂授權攔截器 -->
<result name="login">/Login.jsp</result> <!-- 對應 Action.LOGIN -->
<result name="success">/ShowRole.jsp</result>
</action>
</package>
AuthorizatedAccess.action → Session 無 ROLE → 導向 Login.jsp。Login.jsp 選擇角色(Employee/Manager)提交 → Login 存 Session → chain 到 AuthorizatedAccess。ShowRole.jsp。攔截器將橫切邏輯模組化、標準化,defaultStack 滿足 90% 場景;自訂攔截器配合 ActionInvocation 上下文 API,可實現認證、事務、日誌、審計等任意橫切關注點。善用攔截器,是編寫高內聚、低耦合 Struts 2 應用的關鍵。