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

Struts 2 ValueStack 傳送帶機制深度解析與自定義攔截器實戰

     編輯:Java 相關知識

Struts 2 的 ValueStack 貫穿 Action 整個生命周期,如同傳送帶將屬性值在攔截器鏈中傳遞、變換,最終賦給 Action 實例。本文深度剖析此機制原理,並通過自定義 PropertyInterceptor 實現從屬性文件自動注入屬性值,展示攔截器順序對結果的決定性影響。

ValueStack 傳送帶工作原理

  1. 請求到達 → 創建 Action 實例 → 初始化 ValueStack(頂層節點存放默認屬性值)。
  2. 依次調用攔截器鏈:各攔截器讀取數據源(請求參數、配置參數等),更新 ValueStack 對應屬性。
  3. 調用 Action 方法前,將 ValueStack 頂層屬性值賦給 Action 實例字段。
  4. 同名屬性後配置的攔截器覆蓋先配置的(鏈中靠後者生效)。

默認棧 defaultStack 中 staticParams 在 params 之前,故請求參數會覆蓋配置參數同名屬性。

自定義 PropertyInterceptor 實現

功能:讀取屬性文件(key=value),自動映射到 Action 屬性,支持:

  • 路徑配置(path)、編碼(encoding,默認 UTF-8)
  • Key 中含 . 處理:去除(person.name→personname)或替換分隔符(separator="_" → person_name)
  • 長 Key 映射:<param name="shortName">long.key.name</param>

核心代碼

public class PropertyInterceptor extends AbstractInterceptor {
    // ... 參數設置省略 ...
    
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        ActionConfig config = invocation.getProxy().getConfig();
        Map<String, String> params = config.getParams();
        
        if (params.containsKey("path")) {
            String path = params.get("path");
            String encoding = params.get("encoding") != null ? params.get("encoding") : "UTF-8";
            String separator = params.get("separator") != null ? params.get("separator") : "";
            
            // 加載屬性文件
            Properties props = new Properties();
            InputStream is = new FileInputStream(invocation.getAction().getClass().getResource(path).getPath());
            props.load(new InputStreamReader(is, encoding));
            
            ValueStack stack = invocation.getInvocationContext().getValueStack();
            
            // 遍歷屬性映射到 ValueStack
            for (Object keyObj : props.keySet()) {
                String key = keyObj.toString();
                Object value = props.get(key);
                
                // 1. 直接匹配
                if (!key.contains(".")) stack.setValue(key, value);
                
                // 2. 去除點號匹配
                String noDot = key.replaceAll("\\.", "");
                if (params.containsKey(noDot)) stack.setValue(params.get(noDot), value);
                else if (!key.contains(".")) stack.setValue(noDot, value); // fallback
                
                // 3. 分隔符替換匹配
                if (!separator.isEmpty()) {
                    String withSep = key.replaceAll("\\.", separator);
                    stack.setValue(withSep, value);
                }
                
                // 4. 顯式參數映射
                String mapped = params.get(key);
                if (mapped != null) stack.setValue(mapped, value);
            }
        }
        return invocation.invoke();
    }
}

Action 類示例

public class MyAction {
    private String name;
    private Integer price;
    private String log4jappenderstdout;
    private String log4j_rootLogger;
    private String conversionPattern;
    // getter/setter 省略
    
    public String execute() {
        System.out.println("name: " + name);
        System.out.println("price: " + price);
        // ...
        return null;
    }
}

struts.xml 配置

<package name="struts" extends="struts-default">
  <interceptors>
    <interceptor name="property" class="interceptors.PropertyInterceptor"/>
    <interceptor-stack name="myStack">
      <interceptor-ref name="defaultStack"/>
      <interceptor-ref name="property"/> <!-- 放在最後,生效優先級最高 -->
    </interceptor-stack>
  </interceptors>
  
  <action name="test" class="actions.MyAction">
    <interceptor-ref name="myStack"/>
    <param name="path">/log4j.properties</param>
    <param name="encoding">UTF-8</param>
    <param name="separator">_</param>
    <param name="log4jappenderstdoutlayoutConversionPattern">conversionPattern</param>
  </action>
</package>

測試結果

name: 超人
price: 10000
log4jappenderstdout: org.apache.log4j.ConsoleAppender
log4j_rootLogger: error,stdout
conversionPattern: %d{ABSOLUTE}%5p%c{1}:%L-%m%n

攔截器順序的關鍵影響

配置中 property 放在 defaultStack 之後,其設置的值為最終結果。若交換順序(property 在前),則請求參數或配置參數可覆蓋屬性文件值。這證明:ValueStack 傳送帶上,最後經過的攔截器決定最終值。

總結

  • ValueStack 是 Struts 2 數據流轉核心,理解其「傳送帶」特性對調試、擴展至關重要。
  • 自定義攔截器通過 stack.setValue() 修改 ValueStack,實現靈活數據注入。
  • 攔截器棧順序決定屬性值最終歸屬,配置時務必明確優先級意圖。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved