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

Struts2 ModelDriven 與 Preparable 接口深度解析:解決屬性驅動與預處理難題

     編輯:Java 綜合教學

背景痛點

在 Struts2 開發中,Action 類往往需要大量的屬性與 getter/setter,導致代碼冗餘。同時,某些業務方法(如編輯頁面回顯)需要在執行前進行數據預加載。ModelDriven 與 Preparable 兩大接口正是為了解決這兩類問題而設計。

一、Preparable 接口:預處理機制

1.1 接口定義

public interface Preparable {
    void prepare() throws Exception; // 所有 action 方法執行前調用
}

1.2 精準預處理:prepare{MethodName}()

實現 Preparable 後,Struts2 會根據調用的 action 方法名,嘗試調用對應的 prepareXxx() 方法。優先級:

  1. prepareXxx()(針對具體方法)
  2. prepare()(通用預處理)

若無對應方法則跳過,不拋異常。

二、ModelDriven 接口:模型驅動,告別屬性堆砌

2.1 接口定義

public interface ModelDriven<T> {
    T getModel(); // 返回模型對象實例
}

2.2 核心機制

  • Action 無需定義模型屬性及 getter/setter。
  • Struts2 的參數攔截器會自動調用 getModel() 獲取模型,將請求參數直接注入模型對象。
  • 模型對象通常為領域實體或 DTO,實現屬性複用。

2.3 關鍵約束:getModel() 必須在 prepare() 之前可用

Struts2 默認攔截器棧順序:paramsPrepareParamsStack 中,modelDriven 攔截器在 prepare 之前。但若使用自定義棧或錯誤配置,可能導致 prepareXxx() 中訪問模型屬性為 null。最佳實踐:在基類 prepare() 中實例化模型,確保 getModel() 始終返回非 null。

三、執行順序實戰驗證

3.1 類結構設計

// 基礎 CRUD 抽象類
public abstract class CRUDAction<T> extends ActionSupport implements ModelDriven<T>, Preparable {
    // 留空通用 prepare,由子類實現具體 prepareXxx
    @Override public void prepare() throws Exception { }
    
    // 強制子類實現針對 input 方法的預處理
    public abstract void prepareInput() throws Exception;
}

// 具體業務 Action
public class EmployeeAction extends CRUDAction<Employee> {
    private Employee employee = new Employee(); // 模型實例
    
    @Override public Employee getModel() { return employee; }
    
    @Override public void prepareInput() throws Exception {
        // 編輯頁回顯:根據 id 查詢數據填充 employee
        if (employee.getId() != null) {
            employee = employeeService.get(employee.getId());
        }
    }
    
    public String input() { return INPUT; } // 返回編輯頁
}

3.2 請求 /employee_input.action 時的執行鏈

  1. CRUDAction.prepare()(空實現)
  2. EmployeeAction.prepareInput()(查詢回顯數據)
  3. EmployeeAction.input()(業務邏輯)
  4. EmployeeAction.getModel()(參數注入前獲取模型)

關鍵點:prepareInput() 中查詢到的 employee 實例將被後續參數注入覆蓋表單提交的字段,實現「查詢回顯 + 表單覆蓋」的無縫銜接。

四、進階應用:解決更新時屬性丟失問題

場景:編輯用戶,表單僅包含 username、email,不包含 password、createTime。若直接更新,未提交字段會被設置為 null。

解決方案:在 prepareUpdate() 中先查詢完整對象,再由參數攔截器覆蓋表單字段:

public void prepareUpdate() throws Exception {
    if (model.getId() != null) {
        User existing = userService.get(model.getId());
        // 保留不應被修改的字段
        model.setPassword(existing.getPassword());
        model.setCreateTime(existing.getCreateTime());
    }
}

五、常見坑點與避雷指南

問題原因解決
prepareXxx 中模型屬性為 nullgetModel() 返回 null 或模型未初始化在基類 prepare() 或聲明處實例化模型
雙重參數注入導致覆蓋使用 paramsPrepareParamsStack 會注入兩次預處理中查詢數據,第二次注入自動覆蓋表單字段
ModelDriven 與表單字段名衝突模型屬性名與非模型字段重名使用前綴或分離模型與表單 DTO

六、總結

  • ModelDriven:消除 Action 中大量 getter/setter,實現領域模型與表單數據的直接綁定。
  • Preparable:提供細粒度的預處理鉤子,解決編輯回顯、數據預加載、屬性保護等橫切關注點。
  • 組合使用:基類封裝通用 CRUD 骨架,子類專注業務邏輯,顯著提升開發效率與代碼質量。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved