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

Spring 整合 Struts2:Action 單例與多例配置錯誤導致每次請求創建新實例

     編輯:程式設計綜合問答

問題現象

Spring 3.2.12 + Struts 2.3.20。Action 類配置:


@Component
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) // 顯式聲明單例
@ParentPackage("struts-default")
@Namespace("/test")
public class TestAction { ... }

運行發現:Spring 容器中持有單例 TestAction,但 Struts 每次請求都獲得新實例 (日誌輸出 `this` 和 `spring` 獲取的 bean 內存地址不同)。

根本原因

Struts2-Spring 插件的工作機制:

  1. Struts 接收請求,解析 Action 名稱 (如 `testAction`)。
  2. 通過 `SpringBeanAutowiringInterceptor` 或 `StrutsSpringObjectFactory` 向 Spring 容器**請求名為 `testAction` 的 bean**。
  3. 關鍵點:Spring 根據** bean 名稱 (ID)** 查找,而非類型。若容器中**無同名 bean 定義**,Spring 會**嘗試自動實例化該類** (作為原型 bean) 並返回。
  4. 代碼中 `@Component` 無指定 `value`,bean 名稱默認為類名首字母小寫 `testAction`,但加了 `@Scope(SINGLETON)` 導致 Spring 創建單例。
  5. 然而,Struts 請求時可能觸發了**不同的實例化路徑**,或配置衝突導致 Spring 返回新實例。

正確配置:Action 應為 Prototype (多例)

Struts2 Action 必須是多例 (prototype),因為 Action 實例持有請求狀態 (參數、驗證錯誤等),單例會導致併發數據污染。

修正步驟

  1. 移除 Spring 註解配置:Action 類**不需要** `@Component`、`@Scope` 等 Spring 註解。
  2. 在 struts.xml 中配置 Action (或約定優於配置):

<package name="test" namespace="/test" extends="struts-default">
    <action name="testAction" class="com.me.permission.action.TestAction">
        <result name="success">/index.jsp</result>
    </action>
</package>

或使用約定插件 (需 `struts2-convention-plugin`):


@Action(value = "testAction", results = { @Result(name = "success", location = "/index.jsp") })
public String test() { ... }
// 類上無 Spring 註解
  1. Spring 僅管理 Service/Dao 等業務組件:Action 通過 `@Autowired` 或 `setter` 注入 Service。

public class TestAction {
    @Autowired // 或提供 setter
    private UserService userService; // Service 為單例
    
    private String param; // 請求參數,每次請求新實例隔離
    
    public String test() { ... }
}

為什麼「不配置 Spring 註解」反而能工作?

Struts2 的 `SpringObjectFactory` 邏輯:


// 簡化邏輯
public Object getBean(String beanName) {
    if (applicationContext.containsBean(beanName)) {
        return applicationContext.getBean(beanName); // 返回 Spring 管理的 bean
    } else {
        // 容器無此 bean 定義 → 按 prototype 模式自動實例化類 (需類在掃描路徑)
        return instantiateClass(beanName); 
    }
}
  • **不加 `@Component`** → Spring 容器**無** `testAction` bean 定義 → Struts 請求時 → Spring **自動以 prototype 模式創建新實例** → 符合 Struts2 要求。
  • **加了 `@Component @Scope(SINGLETON)`** → 容器**有**單例 bean → Struts 請求時 → 返回**同一單例實例** → **併發 Bug**。

最佳實踐總結

組件類型Spring ScopeStruts2 管理說明
Action**不由 Spring 管理** (或 prototype)是每請求一實例,持有請求狀態
ServiceSingleton否無狀態業務邏輯,線程安全
Dao/RepositorySingleton否無狀態數據訪問
Controller (Spring MVC)SingletonN/ASpring MVC Controller 設計為單例,參數通過方法傳遞

驗證方法

在 Action 中打印 `this.hashCode()` 或注入 `ApplicationContext` 獲取 bean 對比,連續請求觀察是否同一實例。

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