首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Struts2 Action 訪問 Servlet API 三大方式深度解析:Aware 接口 vs ServletActionContext vs ActionContext

     編輯:Java 綜合教學

前言:Struts2 如何打破與 Servlet API 耦合?

Struts2 核心設計目標之一是Action 與 Servlet API 解耦,使 Action 成為純 POJO,便於單元測試。但實際開發中,往往不可避免地需要訪問 HttpServletRequest、HttpServletResponse、HttpSession、ServletContext。Struts2 提供三種機制滿足需求,本文深度對比其原理、優劣與最佳實踐。

一、方式一:實現 *Aware 接口(依賴注入模式,推薦)

1.1 核心接口對照表

需訪問對象接口注入方法參數類型
HttpServletRequestServletRequestAwaresetServletRequest()HttpServletRequest
HttpServletResponseServletResponseAwaresetServletResponse()HttpServletResponse
HttpSessionSessionAwaresetSession()Map<String, Object> (Session Map)
ServletContextApplicationAwaresetApplication()Map<String, Object> (Application Map)
Request 參數 MapParameterAwaresetParameters()Map<String, String[]>
Request 屬性 MapRequestAwaresetRequest()Map<String, Object> (Request Map)

1.2 實戰代碼

public class UserAction extends ActionSupport 
        implements ServletRequestAware, ServletResponseAware, SessionAware, ApplicationAware {

    private HttpServletRequest request;
    private HttpServletResponse response;
    private Map<String, Object> sessionMap;
    private Map<String, Object> applicationMap;

    // Struts2 容器自動調用注入
    @Override public void setServletRequest(HttpServletRequest request) { this.request = request; }
    @Override public void setServletResponse(HttpServletResponse response) { this.response = response; }
    @Override public void setSession(Map<String, Object> session) { this.sessionMap = session; }
    @Override public void setApplication(Map<String, Object> application) { this.applicationMap = application; }

    public String login() {
        // 1. 直接使用原生 Request/Response
        String ip = request.getRemoteAddr();
        response.setHeader("X-Custom-Header", "Struts2");

        // 2. 使用 Map 操作 Session/Application(無需強轉,線程安全)
        User user = userService.authenticate(getUsername(), getPassword());
        if (user != null) {
            sessionMap.put("LOGIN_USER", user); // 等同 session.setAttribute
            applicationMap.put("ONLINE_COUNT", getOnlineCount() + 1);
            return SUCCESS;
        }
        return ERROR;
    }
}

1.3 原理:攔截器注入

Struts2 默認攔截器棧包含:

  • servletConfig:注入 Request/Response(實現 ServletRequestAware/ResponseAware)
  • sessionAutowiring / applicationAutowiring:創建 SessionMap/ApplicationMap 代理,注入 SessionAware/ApplicationAware

優點:
✅ Action 保持 POJO,無靜態依賴,單元測試極簡單(直接 new Action 再 setXxx)。
✅ Session/Application 以 Map 形式暴露,隱藏 Servlet API 細節,增強可測試性。
✅ 類型安全,IDE 提示友好。
缺點:
❌ 需實現多個接口,代碼略顯冗長。
❌ 注入發生在 Action 實例化後、execute() 前,構造器/字段初始化階段無法使用注入對象。

二、方式二:ServletActionContext 工具類(靜態訪問,慎用)

2.1 核心靜態方法

// 獲取原生 Servlet API 對象
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
HttpSession session = ServletActionContext.getRequest().getSession();
ServletContext application = ServletActionContext.getServletContext();

// 獲取 ActionContext(見方式三)
ActionContext ctx = ServletActionContext.getActionContext();

2.2 原理:ThreadLocal 綁定

ServletActionContext 內部維護 ThreadLocal<ActionContext>,在請求處理線程中綁定當前 ActionContext,其中持有 Request/Response 等對象。

2.3 優缺點極其鮮明

優點缺點
  • 任意位置(非 Action 類、工具類、攔截器)直接調用
  • 無需實現接口,代碼極少
  • 獲取原生 Servlet API,功能無限制
  • 強依賴 Servlet 容器,單元測試極難(需模擬 ThreadLocal 上下文)
  • 破壞 Struts2 解耦設計初衷
  • 靜態調用隱藏依賴,降低代碼可讀性
  • 併發環境下 ThreadLocal 洩漏風險

適用場景:非 Action 類(工具類、攔截器、過濾器)臨時獲取 Request/Response;遺留代碼快速遷移;原型驗證。核心業務 Action 禁用。

三、方式三:ActionContext 上下文對象(Map 視圖,靈活)

3.1 核心 API


ActionContext ctx = ActionContext.getContext(); // 獲取當前線程上下文

// 1. 獲取/設置 Request 參數 (Map)
Map<String, String[]> params = ctx.getParameters();
params.put("newParam", new String[]{"value"}); // 設置參數

// 2. 獲取/設置 Request 屬性 (Map)
Map<String, Object> requestMap = (Map) ctx.get("request"); // 或 ctx.getRequest()
// 實際推薦:實現 RequestAware 獲取 Map

// 3. 獲取/設置 Session (Map)
Map<String, Object> sessionMap = ctx.getSession();
sessionMap.put("key", value); // 等同 session.setAttribute

// 4. 獲取/設置 Application (Map)
Map<String, Object> appMap = ctx.getApplication();
appMap.put("key", value); // 等同 application.setAttribute

3.2 特點

  • 以Map 視圖統一訪問 Request/Session/Application 屬性,完全屏蔽 Servlet API。
  • 支持在任意持有 ActionContext 引用的地方訪問(攔截器、結果類、標簽庫)。
  • getParameters() 返回參數 Map,可直接修改參數值(影響后續類型轉換)。

四、三大方式綜合對比與選型指南

維度Aware 接口 (推薦)ServletActionContextActionContext
解耦程度⭐⭐⭐⭐⭐ (完全 POJO)⭐ (強耦合 Servlet)⭐⭐⭐⭐ (Map 視圖)
可測試性⭐⭐⭐⭐⭐ (直接 setXxx)⭐ (需模擬 ThreadLocal)⭐⭐⭐ (構造 Map 注入)
功能完整性⭐⭐⭐⭐ (Map 限制部分操作)⭐⭐⭐⭐⭐ (原生 API)⭐⭐⭐ (Map 視圖)
代碼侵入性實現多接口零侵入 (靜態調用)低 (獲取上下文)
適用範圍所有 Action非 Action 類/攔截器/工具類攔截器/結果類/標簽/Action
線程安全安全 (請求作用域實例)ThreadLocal 風險安全 (請求作用域 Map)

五、最佳實踐建議

  1. 核心 Action 必用 Aware 接口:實現 ServletRequestAware、SessionAware 等,保證可測試性、解耦性。
  2. 攔截器/工具類用 ServletActionContext:非 Action 組件無法實現 Aware,靜態獲取是唯一選擇。
  3. 需批量操作屬性/參數用 ActionContext:Map 視圖便於遍歷、清理、複製屬性。
  4. 避免在構造器/字段初始化使用注入對象:注入發生在 prepare() 前、execute() 前,構造器不可用。
  5. Session/Application 操作優先用 Map:sessionMap.put(key, val) 而非 session.setAttribute(key, val),保持解耦。

六、源碼級原理:Aware 接口注入鏈路


// struts-default.xml 定義的攔截器棧片段
<interceptor-stack name="defaultStack">
    <interceptor-ref name="servletConfig"/>       <!-- 注入 Request/Response -->
    <interceptor-ref name="prepare"/>
    <interceptor-ref name="checkbox"/>
    <interceptor-ref name="params"/>             <!-- 參數注入 -->
    <interceptor-ref name="conversionError"/>
    <interceptor-ref name="sessionAutowiring"/>  <!-- 創建 SessionMap 並注入 SessionAware -->
    <interceptor-ref name="applicationAutowiring"/> <!-- 創建 ApplicationMap 並注入 ApplicationAware -->
    ...
</interceptor-stack>

// ServletConfigInterceptor 核心邏輯
public class ServletConfigInterceptor extends AbstractInterceptor {
    public String intercept(ActionInvocation invocation) {
        Action action = invocation.getAction();
        if (action instanceof ServletRequestAware) {
            ((ServletRequestAware) action).setServletRequest(ServletActionContext.getRequest());
        }
        if (action instanceof ServletResponseAware) {
            ((ServletResponseAware) action).setServletResponse(ServletActionContext.getResponse());
        }
        if (action instanceof ParameterAware) {
            ((ParameterAware) action).setParameters(ServletActionContext.getRequest().getParameterMap());
        }
        return invocation.invoke();
    }
}

七、總結

Struts2 提供三種訪問 Servlet API 的方式,本質是解耦與便利的權衡。Aware 接口是正統、推薦、可測試的選擇;ServletActionContext 是打破規則的緊急通道;ActionContext 是屏蔽 Servlet API 的統一 Map 視圖。架構師應制定規約:Action 層強制 Aware,非 Action 層酌情静態,統一屬性操作用 Map,方能構建可維護、可測試的 Struts2 應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved