Struts2 Action 默認非 Servlet API 依賴,如何獲取 request、session、application?提供兩種標準方案。
public class UserAction extends ActionSupport {
private User user;
public String register() {
// 獲取 Map 形式的 request/session/application
Map request = (Map) ActionContext.getContext().get("request");
Map session = ActionContext.getContext().getSession();
Map application = ActionContext.getContext().getApplication();
// 存值
request.put("username", user.getName());
session.put("loginUser", user);
return SUCCESS;
}
// getter/setter...
}
原理:ActionContext 是線程局部變量,每個請求獨立,內部持有 ServletRequestMap 等包裝類,操作最終委託給原生 Servlet API。
public class UserAction extends ActionSupport
implements RequestAware, SessionAware, ApplicationAware {
private Map request;
private Map session;
private Map application;
private User user;
public String register() {
request.put("username", user.getName());
session.put("loginUser", user);
return SUCCESS;
}
// 必須實現的 setter,Struts2 自動注入
@Override public void setRequest(Map request) { this.request = request; }
@Override public void setSession(Map session) { this.session = session; }
@Override public void setApplication(Map application) { this.application = application; }
// getter/setter...
}
接口定義在 org.apache.struts2.interceptor 包:
RequestAware → setRequest(Map)SessionAware → setSession(Map)ApplicationAware → setApplication(Map)ParameterAware → setParameters(Map) (獲取所有請求參數)ServletRequestAware → setServletRequest(HttpServletRequest) (直接拿原生對象)ServletResponseAware → setServletResponse(HttpServletResponse)<!-- EL 表達式 (推薦) -->
${requestScope.username}
${sessionScope.loginUser.name}
<!-- Struts2 標簽 -->
<s:property value="#request.username" />
<s:property value="#session.loginUser.name" />
| 維度 | ActionContext | 接口注入 |
|---|---|---|
| 代碼侵入性 | 低(任意位置調用) | 需實現接口、聲明字段 |
| 單元測試 | 難(依賴 ThreadLocal,需手動構建上下文) | 易(直接 new Action(), setRequest(mockMap)) |
| 類型安全 | Map | 同樣,但字段可復用 |
| 生命周期 | 隨時獲取當前請求上下文 | 請求開始時注入,請求結束失效 |
| 推薦場景 | 快速原型、工具方法、攔截器 | 正式業務 Action、需單測項目 |
public class FileAction extends ActionSupport implements ServletRequestAware, ServletResponseAware {
private HttpServletRequest request;
private HttpServletResponse response;
@Override public void setServletRequest(HttpServletRequest request) { this.request = request; }
@Override public void setServletResponse(HttpServletResponse response) { this.response = response; }
public String upload() {
// 直接用 request.getInputStream()、response.getOutputStream()
// 獲取真實路徑:request.getServletContext().getRealPath("/upload")
}
}
⚠️ 注意:直接依賴 Servlet API 會降低可測試性,非必要不建議。
請求到達
↓
FilterDispatcher / StrutsPrepareAndExecuteFilter
↓
創建 ActionContext (ThreadLocal)
↓
實例化 Action (或從 Spring 獲取)
↓
【接口注入】調用 setRequest/setSession/setApplication...
↓
調用 Action 方法 (execute / xxx)
↓
結果視圖渲染 (JSP/FTL/JSON)
↓
ActionContext 清理 (ThreadLocal remove)
↓
響應返回
ActionContext.getContext()${requestScope.xxx}),不寫 Scriptlet掌握這兩種訪問方式的本質區別與適用場景,是 Struts2 開發從「能跑通」到「工程化」的關鍵一步。