首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Struts2動態方法調用陷阱:通配符映射導致多Action誤觸發

     編輯:程式設計疑難解答

Struts2配置陷阱:通配符映射導致單請求觸發多Action的根因與修復

Struts2動態方法調用(DMI)配合通配符映射時,極易出現「一個請求匹配多個Action」的隱蔽bug。本文通過實戰案例深度剖析struts.xml映射規則優先級、通配符捕獲機制,提供徹底解決方案。

問題現場還原

前端請求

$http({
    method: "POST",
    url: "/AzraelShop/Mgr-getAllUsers",  // 目標:getAllUsersAction
    data: {}
})

struts.xml配置(有序)

<package name="mgr" namespace="/Mgr" extends="struts-default">
    <!-- 配置1:精確匹配 Mgr-*User -->
    <action name="Mgr-*User" class="com.azrael.controller.MgrController" method="{1}UserAction">
        <result type="json">
            <param name="includeProperties">user.*</param>
        </result>
    </action>
    
    <!-- 配置2:通用通配符 *-* -->
    <action name="*-*" class="com.azrael.controller.{1}Controller" method="{2}Action">
        <result type="json"/>
    </action>
</package>

異常現象

請求/Mgr-getAllUsers同時觸發:

  1. MgrController.getAllUsersAction()(預期)
  2. MgrController.getUserAction()(非預期,因getAllUsers匹配{1}=getAll, {2}=Users)

根因深度剖析

Struts2 Action映射匹配規則

  1. 順序優先:struts.xml中<action>定義順序決定嘗試順序
  2. 通配符貪婪匹配:*-*能匹配任意包含連字符的字符串
  3. 動態方法調用(DMI):method="{2}Action"將第二個通配符組作為方法名後綴

匹配過程推演

請求路徑:/Mgr-getAllUsers

配置1:name="Mgr-*User"
  模式:Mgr- + [任意字符] + User
  匹配:getAllUsers 以 User 結尾?否 → 匹配失敗

配置2:name="*-*"
  模式:[任意字符] + - + [任意字符]
  分組:{1}="Mgr", {2}="getAllUsers"
  映射:class="MgrController", method="getAllUsersAction"
  → 匹配成功,執行 getAllUsersAction()

  但同時:{2}="getAllUsers" 也被 DML 解析為方法名
  Struts2 內部邏輯嘗試調用 getUserAction()(getter 慣例)

核心問題:DMI與Getter命名衝突

Struts2約定:getXxx()方法會被識別為屬性獲取器。當{2}以get開頭時:

  • getAllUsers → 嘗試調用getAllUsersAction()(正確)
  • 同時識別為get前綴 → 嘗試調用getUserAction()(錯誤,將AllUsers視為屬性名)

三種徹底解決方案

方案一:調整配置順序 + 精確模式(推薦)

<package name="mgr" namespace="/Mgr" extends="struts-default">
    <!-- 1. 精確業務映射優先 -->
    <action name="getAllUsers" class="com.azrael.controller.MgrController" method="getAllUsersAction">
        <result type="json"><param name="includeProperties">user.*</param></result>
    </action>
    <action name="getUser" class="com.azrael.controller.MgrController" method="getUserAction">
        <result type="json"/>
    </action>
    
    <!-- 2. 通用落後兜底,限制模式 -->
    <action name="*-*" class="com.azrael.controller.{1}Controller" method="{2}Action">
        <result type="json"/>
        <!-- 可添加 excludeMethods 防止誤觸 -->
    </action>
</package>

方案二:禁用DMI使用註解映射

// MgrController.java
@Namespace("/Mgr")
@Action(value="getAllUsers", results={@Result(type="json", params={"includeProperties","user.*"})})
public String getAllUsersAction() { ... }

@Action(value="getUser", results={@Result(type="json")})
public String getUserAction() { ... }

在struts.xml中僅保留<package>掃描配置,避免通配符衝突。

方案三:DMI排除前綴配置

<constant name="struts.action.excludeMethods" value="get*,set*,is*" />

在struts.properties或<constant>中配置,禁止get開頭的方法被DMI調用。

最佳實踐總結

  1. 拒絕過度通配:業務Action採用顯式映射,通配符僅作兜底
  2. 命名規範:Action方法避免get/set/is開頭,改用load/find/query/do
  3. 命名空間隔離:不同功能模塊使用不同namespace
  4. 啟用嚴格模式:struts.mapper.alwaysSelectFullNamespace=true
  5. 單元測試覆蓋:編寫Action映射測試,驗證請求路徑唯一性

Struts2通配符映射雖便捷,但「隱式行為」是生產事故高發區。顯式配置雖繁瑣,卻是系統穩定性的基石。

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