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

WebWork 2 源碼深度解析 (一):ServletDispatcher 請求解耦與 ParametersInterceptor 屬性綁定

     編輯:Java 相關知識

前言

WebWork 2 最大創新在於 **徹底解耦 Servlet API**,將 HTTP 請求轉為純 Java Map 傳入 XWork 核心層。本文分析 ServletDispatcher 請求轉換與 ParametersInterceptor 屬性綁定機制。

一、ServletDispatcher — Web 層進入點

createContextMap() — HTTP 請求 Map 化

public static HashMap createContextMap(Map requestMap, Map parameterMap,
        Map sessionMap, Map applicationMap,
        HttpServletRequest request, HttpServletResponse response,
        ServletConfig servletConfig) {

    HashMap extraContext = new HashMap();
    extraContext.put(ActionContext.PARAMETERS, parameterMap);
    extraContext.put(ActionContext.SESSION, sessionMap);
    extraContext.put(ActionContext.APPLICATION, applicationMap);
    extraContext.put(ActionContext.LOCALE, (locale == null) ? request.getLocale() : locale);
    extraContext.put(HTTP_REQUEST, request);      // 保留原始請求 (少數需直接存取)
    extraContext.put(HTTP_RESPONSE, response);
    extraContext.put(SERVLET_CONFIG, servletConfig);
    extraContext.put(ComponentInterceptor.COMPONENT_MANAGER,
                     request.getAttribute(ComponentManager.COMPONENT_MANAGER_KEY));

    // 便利鍵值,供 OGNL 直接存取
    extraContext.put("request", requestMap);
    extraContext.put("session", sessionMap);
    extraContext.put("application", applicationMap);
    extraContext.put("parameters", parameterMap);
    extraContext.put("attr", new AttributeMap(extraContext)); // 統一存取 request/session/application

    return extraContext;
}

serviceAction() — 請求處理主流程

public void serviceAction(HttpServletRequest request, HttpServletResponse response,
        String namespace, String actionName,
        Map requestMap, Map parameterMap, Map sessionMap, Map applicationMap) {

    HashMap extraContext = createContextMap(...);

    // 值棧傳遞 (支援攔截器間共享)
    OgnlValueStack stack = (OgnlValueStack) request.getAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY);
    if (stack != null) {
        extraContext.put(ActionContext.VALUE_STACK, new OgnlValueStack(stack)); // 複製棧
    }

    try {
        // 核心委派:ActionProxyFactory → ActionProxy → ActionInvocation
        ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(namespace, actionName, extraContext);
        request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY, proxy.getInvocation().getStack());
        proxy.execute(); // 執行攔截器鏈 + Action + Result
    } catch (ConfigurationException e) {
        sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
    } catch (Exception e) {
        sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
    } finally {
        // 還原原始值棧
        if (stack != null) request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY, stack);
    }
}

二、ActionProxy 與 ActionProxyFactory

  • ActionProxy 介面定義 execute(), getInvocation(), getConfig()。
  • DefaultActionProxy 實作:持有 ActionInvocation、ActionConfig、executeResult 標誌。
  • ActionProxyFactory 抽象工廠,DefaultActionProxyFactory 實作建立 Proxy 與 Invocation。

三、ParametersInterceptor — Map 到物件屬性綁定

public class ParametersInterceptor extends AroundInterceptor {
    protected void before(ActionInvocation invocation) throws Exception {
        // 1. 排除標記為 NoParameters 的 Action
        if (invocation.getAction() instanceof NoParameters) return;

        // 2. 取得參數 Map
        Map parameters = ActionContext.getContext().getParameters();
        if (parameters == null) return;

        // 3. 設定 OGNL 運作上下文旗標
        ActionContext ctx = invocation.getInvocationContext();
        ctx.put(InstantiatingNullHandler.CREATE_NULL_OBJECTS, Boolean.TRUE);  // 自動建立巢狀物件
        ctx.put(XWorkMethodAccessor.DENY_METHOD_EXECUTION, Boolean.TRUE);     // 禁止任意方法執行
        ctx.put(XWorkConverter.REPORT_CONVERSION_ERRORS, Boolean.TRUE);       // 收集類型轉換錯誤

        try {
            OgnlValueStack stack = ActionContext.getContext().getValueStack();
            for (Map.Entry entry : parameters.entrySet()) {
                String name = entry.getKey().toString();
                // 4. 安全過濾:排除 OGNL 特殊字元 (= , #) 防止注入
                if (acceptableName(name)) {
                    Object value = entry.getValue();
                    stack.setValue(name, value); // OGNL 設值:自動類型轉換、巢狀屬性建立
                }
            }
        } finally {
            // 5. 還原旗標
            ctx.put(InstantiatingNullHandler.CREATE_NULL_OBJECTS, Boolean.FALSE);
            ctx.put(XWorkMethodAccessor.DENY_METHOD_EXECUTION, Boolean.FALSE);
            ctx.put(XWorkConverter.REPORT_CONVERSION_ERRORS, Boolean.FALSE);
        }
    }

    protected boolean acceptableName(String name) {
        return name.indexOf('=') == -1 && name.indexOf(',') == -1 && name.indexOf('#') == -1;
    }
    protected void after(ActionInvocation dispatcher, String result) {}
}

四、關鍵設計亮點

特性實現方式優勢
Servlet 解耦ServletDispatcher 將 request/session/parameters 轉為 Map核心層可單元測試、移植非 Servlet 環境 (Portlet, CLI, Test)
OGNL 統一存取ValueStack + OGNL 表達式單一語法存取 Action、Model、Request、Session、Application
類型轉換集中化XWorkConverter + 自訂 TypeConverter字串→日期、枚舉、集合、自訂物件 統一處理
安全防護ParametersInterceptor 過濾 OGNL 特殊字元、DENY_METHOD_EXECUTION防止 OGNL 注入攻擊 (如 Struts 2 歷史漏洞根源)
自動物件建立InstantiatingNullHandleruser.address.city 自動 new Address() 無需手動初始化
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved