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

Struts 2 核心架構深度解析:FilterDispatcher、ActionProxy、攔截器鏈與結果視圖渲染全流程

     編輯:Java 綜合教學

設計目標與技術優勢

  • MVC 徹底分離:Action 為純 POJO,無 Servlet API 耦合,易測試、易復用。
  • 攔截器鏈:橫切關注點 (日誌、驗證、事務、文件上傳、I18n) 插件化組裝。
  • OGNL 表達式語言:強大的屬性存取、集合投影、類型轉換,實現「值棧」數據流動。
  • 插件化架構:REST、JSON、JFreeChart、Tiles、Spring、Hibernate 等無縫整合。

請求處理全流程 (核心類職責)

HTTP Request
    │
    ▼
┌─────────────────────────────────────┐
│ 1. FilterDispatcher (核心控制器)     │
│    ├─ 初始化 DispatcherUtils        │
│    ├─ ActionMapper 決定是否映射 Action│
│    ├─ ActionProxyFactory 創建 ActionProxy│
│    └─ ActionProxy.execute() 觸發調用 │
└─────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────┐
│ 2. ActionProxy (代理)                │
│    ├─ ConfigurationManager 讀 struts.xml│
│    ├─ 創建 ActionInvocation          │
│    └─ 調用 ActionInvocation.invoke() │
└─────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────┐
│ 3. ActionInvocation (調度核心)        │
│    ├─ 按配置順序調用 Interceptor     │
│    ├─ 前置攔截器 (pre)               │
│    ├─ 調用 Action.execute()          │
│    ├─ 後置攔截器 (post)              │
│    └─ 根據 Result 類型渲染視圖       │
└─────────────────────────────────────┘
    │
    ▼
HTTP Response

核心組件深度剖析

1. FilterDispatcher (struts2-core 中 FilterDispatcher 類)

  • 實現 javax.servlet.Filter,web.xml 配置為 /*。
  • doFilter() 關鍵邏輯:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

    DispatcherUtils du = DispatcherUtils.getInstance();
    du.prepare(request, response); // locale、encoding、multipart 處理

    ActionMapper mapper = ActionMapperFactory.getMapper();
    ActionMapping mapping = mapper.getMapping(request); // 解析 namespace/actionName

    if (mapping == null) { // 靜態資源或非 Struts 請求
        chain.doFilter(request, response);
        return;
    }

    // 核心調用鏈
    ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(
        mapping.getNamespace(), mapping.getName(), extraContext);
    proxy.execute(); // 觸發 ActionInvocation
}

2. ActionInvocation (DefaultActionInvocation 實現)

  • 持有 List<Interceptor> interceptors、ActionProxy proxy、ValueStack stack。
  • invoke() 遞歸調用攔截器鏈:
public String invoke() throws Exception {
    if (interceptors.hasNext()) {
        InterceptorMapping im = interceptors.next();
        Interceptor interceptor = im.getInterceptor();
        return interceptor.intercept(this); // 責任鏈模式
    }
    // 所有攔截器執行完,執行 Action
    return proxy.execute(); // 實際調用 Action 方法
}
  • 攔截器在 intercept(ActionInvocation invocation) 中決定是否 invocation.invoke() 繼續向後,實現前置/後置邏輯。
  • 3. 內建攔截器清單 (struts-default.xml 節選)

    4. 自定義攔截器實現

    public class AuditInterceptor implements Interceptor {
        private static final Logger LOG = LoggerFactory.getLogger(AuditInterceptor.class);
    
        @Override public void init() {}
        @Override public void destroy() {}
    
        @Override
        public String intercept(ActionInvocation invocation) throws Exception {
            String actionName = invocation.getProxy().getActionName();
            long start = System.currentTimeMillis();
            try {
                return invocation.invoke(); // 繼續鏈
            } finally {
                long cost = System.currentTimeMillis() - start;
                LOG.info("Action [{}] executed in {} ms", actionName, cost);
            }
        }
    }

    註冊使用:

    <package name="default" extends="struts-default">
      <interceptors>
        <interceptor name="audit" class="com.example.AuditInterceptor"/>
        <interceptor-stack name="myStack">
          <interceptor-ref name="defaultStack"/> <!-- 包含 params, validation, workflow 等 -->
          <interceptor-ref name="audit"/>
        </interceptor-stack>
      </interceptors>
      <default-interceptor-ref name="myStack"/>
      <action name="User_*" class="com.example.UserAction" method="{1}">
        <result>/user/{1}.jsp</result>
      </action>
    </package>

    5. 結果類型與視圖渲染

    名稱類功能
    paramsParametersInterceptor請求參數注入 Action 屬性 (OGNL)
    validationAnnotationValidationInterceptorJSR-303/註解驗證
    workflowDefaultWorkflowInterceptor驗證失敗自動返回 input 結果
    fileUploadFileUploadInterceptor多部分文件上傳處理
    i18nI18nInterceptor區域語言切換
    servletConfigServletConfigInterceptor注入 ServletRequest/Response/Session
    preparePrepareInterceptor調用 Preparable.prepare() 預處理
    modelDrivenModelDrivenInterceptorModelDriven 介面模型驅動
    tokenTokenInterceptor表單重複提交防護
    timerTimerInterceptorAction 執行耗時統計
    exceptionExceptionMappingInterceptor異常映射到結果頁
    Result Type類用途
    dispatcher (預設)ServletDispatcherResultJSP/HTML 轉發
    redirectServletRedirectResultHTTP 302 重定向
    redirectActionServletActionRedirectResult重定向到另一 Action
    jsonJSONResult (struts2-json-plugin)Action 屬性序列化為 JSON
    streamStreamResult文件下載、圖片輸出、二進制流
    freemarker/velocityFreeMarkerResult/VelocityResult模板引擎渲染

    值棧 與 OGNL 表達式

    • ValueStack:線程局部棧 (ThreadLocal<ValueStack>),存放 Action 實例、Model、請求參數、Session/Application 屬性。
    • OGNL 表達式在 JSP/FTL 中存取:<s:property value="user.name"/>、<s:iterator value="orders.{? #this.status=='PAID'}"/> (投影過濾)。
    • <s:form>、<s:textfield name="user.email"/> 自動綁定到 Action 的 getUser().setEmail()。

    常見問題與調優

    1. OGNL 安全漏洞:嚴禁直接執行用戶輸入表達式,升級至 2.5.30+,配置 struts.ognl.enableOGNLCaching=true、struts.ognl.allowStaticMethodAccess=false。
    2. 多文件上傳記憶體溢出:調整 struts.multipart.maxSize、struts.multipart.saveDir、使用流式處理。
    3. 攔截器順序錯誤:params 必須在 validation 之前,否則屬性未注入驗證失效。
    4. Action 非線程安全:Struts 2 預設每請求創建新 Action 實例 (prototype),避免併發問題;若配置 Spring 單例需自行保證線程安全。

    總結

    Struts 2 以 FilterDispatcher 入口 → ActionProxy 代理 → ActionInvocation 責任鏈 → Interceptor 橫切 → OGNL 值棧 → Result 視圖 為核心流程。掌握攔截器鏈組裝、OGNL 表達式、Result 類型選擇,即可駕馭企業級 Web 應用開發。

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