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

Struts 實現每請求一個 Action 實例:基於 BaseAction 原型模式的線程安全方案

     編輯:Java 相關知識

問題背景

Struts 1.x 預設 Action 為單例,多線程共享實例,若 Action 含有成員變量則會發生線程安全問題。雖官方建議「Action 無狀態」,但實際開發中難免需保存請求相關狀態。

解決方案:BaseAction + 原型模式

利用 Java 反射在 execute 中動態創建當前 Action 子類新實例,委派給 doExecute 處理,實現「每請求一實例」。

public abstract class BaseAction extends Action {
    public ActionForward execute(ActionMapping mapping, ActionForm form,
                                 HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        // 每次請求創建新實例,隔離線程狀態
        BaseAction action = (BaseAction) this.getClass().newInstance();
        return action.doExecute(mapping, form, request, response);
    }

    protected abstract ActionForward doExecute(ActionMapping mapping, ActionForm form,
                                               HttpServletRequest request, HttpServletResponse response)
            throws Exception;
}

// 具體 Action 實現
public class ActionA extends BaseAction {
    // 可安全使用成員變量,因為每請求一實例
    private String requestId;

    protected ActionForward doExecute(ActionMapping mapping, ActionForm form,
                                      HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        this.requestId = request.getParameter("id");
        // 業務邏輯...
        return mapping.findForward("success");
    }
}

原理分析

  • this.getClass() 獲取實際子類 Class 對象(如 ActionA.class)。
  • newInstance() 呼叫無參構造器創建新對象(需保證子類有 public 無參構造器)。
  • 新實例僅服務於當前請求,請求結束即可被 GC 回收,天然線程安全。

延伸:ThreadLocal 實現 ActionContext 模式

若想像 WebWork 2 透過 ActionContext.getContext() 獲取 Request/Session,可在 BaseAction 中將 ActionMapping、HttpServletRequest 等放入 ThreadLocal,提供靜態輔助類存取:

public class ActionContextHolder {
    private static final ThreadLocal ctx = new ThreadLocal<>();

    public static void setContext(ActionContext c) { ctx.set(c); }
    public static ActionContext getContext() { return ctx.get(); }
    public static void clear() { ctx.remove(); }
}

在 BaseAction.execute 開頭設定,finally 清理。

優缺點權衡

優點缺點
徹底解決線程安全,成員變量隨意用每請求反射創建對象,微小效能開銷(通常可忽略)
代碼結構清晰,符合 OO 直覺需繼承 BaseAction,侵入性稍強
易於整合 ThreadLocal 上下文模式Struts 1.x 設計初衷為單例,違背框架約定

結語

此模式在 Struts 1.x 專案中廣泛使用,為遷移至 Struts 2 / Spring MVC(原生每請求一實例)前的過渡期提供了優雅的線程安全解法。

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