前言
Struts2 Action 默認與 Servlet API 解耦,但實際開發中往往需要訪問 Request、Session、Application 等 Servlet 核心對象。本文系統梳理 Struts2 提供的三種訪問機制,對比優缺點,給出最佳實踐建議。
一、核心機制對比總覽
| 方式 | 核心類/接口 | 耦合度 | 適用場景 | 推薦指數 |
| ActionContext 上下文 | ActionContext | 低(Map 代理) | 只需讀寫屬性值,不依賴 Servlet 原生 API | ⭐⭐⭐⭐ |
| Aware 接口注入 | ServletRequestAware 等 | 中(接口依賴) | 需直接操作原生 Servlet API 對象 | ⭐⭐⭐⭐⭐ |
| 工具類靜態獲取 | ServletActionContext | 高(靜態依賴) | 非 Action 類(Service、Util、攔截器)需訪問 | ⭐⭐⭐ |
二、方式一:ActionContext 上下文訪問(推薦)
核心原理
Struts2 將 Servlet API 封裝為 Map 結構,Action 通過 ActionContext 獲取代理 Map,間接操作底層 Servlet 對象,實現解耦。
常用 API
| 方法 | 返回類型 | 對應 Servlet API | 說明 |
| getContext() | ActionContext | - | 靜態方法獲取當前線程上下文 |
| getParameters() | Map | request.getParameterMap() | 獲取所有請求參數 |
| getSession() | Map | HttpSession | 操作 Session 屬性(讀寫/移除) |
| getApplication() | Map | ServletContext | 操作 Application 屬性 |
| get(key) | Object | request.getAttribute(key) | 獲取 Request 屬性 |
| put(key, value) | void | request.setAttribute(key, value) | 設置 Request 屬性 |
代碼示例
public class UserAction extends ActionSupport {
public String login() {
// 1. 獲取上下文
ActionContext ctx = ActionContext.getContext();
// 2. 操作 Session(Map 方式)
Map session = ctx.getSession();
session.put("loginUser", currentUser);
// 3. 操作 Application
Map application = ctx.getApplication();
Integer onlineCount = (Integer) application.get("onlineCount");
application.put("onlineCount", onlineCount == null ? 1 : onlineCount + 1);
// 4. 獲取請求參數
Map params = ctx.getParameters();
String username = ((String[]) params.get("username"))[0];
return SUCCESS;
}
}
優缺點分析
- ✅ 完全解耦 Servlet API,Action 單元測試極其簡單(只需構造 Map)
- ✅ 代碼簡潔,類 Map 操作符合 Java 習慣
- ❌ 無法直接訪問 Servlet 原生對象的高級特性(如
request.getInputStream()、response.sendRedirect())
- ❌ Session/Application Map 非線程安全,高併發下需注意
三、方式二:Aware 接口注入(最常用)
核心接口
| 接口 | 注入對象 | 典型用途 |
| ServletRequestAware | HttpServletRequest | 獲取請求頭、輸入流、Request 屬性、轉發 |
| ServletResponseAware | HttpServletResponse | 設置響應頭、輸出流、重定向、Cookie |
| ServletContextAware | ServletContext | 讀取配置文件、獲取真實路徑、應用級屬性 |
| SessionAware | Map<String, Object> | 操作 Session(Struts2 代理 Map,非原生 HttpSession) |
| PrincipalAware | Principal | 獲取當前登錄用戶主體信息 |
實現示例
public class FileUploadAction extends ActionSupport
implements ServletRequestAware, ServletResponseAware, ServletContextAware {
private HttpServletRequest request;
private HttpServletResponse response;
private ServletContext servletContext;
// Struts2 通過 Setter 注入(非構造器注入)
@Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
@Override
public void setServletResponse(HttpServletResponse response) {
this.response = response;
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public String upload() {
// 直接使用原生 API
String realPath = servletContext.getRealPath("/uploads");
Part filePart = request.getPart("file"); // Servlet 3.0+ 文件上傳
// ...
response.setContentType("application/json");
response.getWriter().write("{\"success\":true}");
return NONE; // 已手動輸出響應
}
}
注意事項
- 實現接口後,Struts2 會在 Action 實例創建後、調用 execute() 前自動調用 Setter 注入
- Action 必須是**原型模式**(prototype,默認),否則多線程共享同一實例會導致線程安全問題
- 單元測試時需手動設置依賴,或使用 Mock 框架(Mockito)模擬
四、方式三:ServletActionContext 工具類(特殊場景)
靜態方法一覽
| 方法 | 返回類型 | 說明 |
| getRequest() | HttpServletRequest | 獲取當前請求對象 |
| getResponse() | HttpServletResponse | 獲取當前響應對象 |
| getServletContext() | ServletContext | 獲取應用上下文 |
| getPageContext() | PageContext | 獲取 JSP 頁面上下文 |
| getContext() | ActionContext | 等同於 ActionContext.getContext() |
適用場景
- 非 Action 類中訪問 Servlet API:Service 層、工具類、自定義攔截器、監聽器
- 舊代碼重構過渡期臨時使用
代碼示例
public class ImageUtil {
public static void writeImage(HttpServletResponse response, BufferedImage image) {
try {
// 直接靜態獲取響應對象(不推薦在 Service 中這樣做,破壞分層)
ServletActionContext.getResponse().setContentType("image/jpeg");
ImageIO.write(image, "JPEG", ServletActionContext.getResponse().getOutputStream());
} catch (IOException e) {
throw new RuntimeException("圖片輸出失敗", e);
}
}
}
⚠️ 強烈不建議在 Service/DAO 層使用
破壞分層架構,導致業務邏輯難以單元測試,耦合 Web 容器。正確做法:Controller 接收 Request/Response,提取參數傳給 Service,Service 返回結果對象,Controller 再寫入 Response。
五、最佳實踐總結
- 首選 ActionContext Map 方式:簡單屬性讀寫、Session/Application 操作,零耦合,測試友好
- 次選 Aware 接口注入:需直接操作原生 Servlet API(流、重定向、Cookie、轉發)時使用
- 禁用 ServletActionContext:除非在攔截器、監聽器、工具類等非 Action 環境中被迫使用
- Action 必須配置為 prototype 作用域(struts.xml 默認即是),防止線程安全問題
- 單元測試策略:
- ActionContext 方式:構造 Map 注入
- Aware 接口方式:Mockito mock HttpServletRequest/Response