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

Java 反射實現 JavaBean 屬性自動拷貝:從手動 Set/Get 到通用工具類

     編輯:Java 相關知識

在 Struts 開發中,ActionForm 與領域模型 (Entity/DTO) 之間頻繁需要屬性拷貝。手動編寫大量 setXxx(getXxx()) 既繁瑣又易出錯。Apache Commons BeanUtils 提供 BeanUtils.copyProperties(),但理解其底層反射原理並能手寫簡化版,有助於排查類型轉換、只讀屬性等疑難問題。本文演示基於反射的屬性拷貝核心邏輯並給出完整可運行示例。

核心工具類:BeanUtil2

import java.lang.reflect.Method;

public class BeanUtil2 {
    /**
     * 將 obj1 的所有可讀屬性 (getter) 拷貝至 obj2 的同名可寫屬性 (setter)
     * @param src 源對象
     * @param dest 目標對象 (將被修改並返回)
     * @return 修改後的 dest
     * @throws Exception 反射調用異常
     */
    public static Object copyBeanToBean(Object src, Object dest) throws Exception {
        Method[] srcMethods = src.getClass().getMethods();
        Method[] destMethods = dest.getClass().getMethods();

        for (Method srcM : srcMethods) {
            String srcName = srcM.getName();
            if (!srcName.startsWith("get")) continue; // 僅處理 getter
            String propSuffix = srcName.substring(3); // 去掉 "get"

            for (Method destM : destMethods) {
                String destName = destM.getName();
                if (!destName.startsWith("set")) continue; // 僅處理 setter
                if (!destName.substring(3).equals(propSuffix)) continue; // 屬性名匹配

                // 調用 src getter
                Object value = srcM.invoke(src, (Object[]) null);
                // 調用 dest setter
                destM.invoke(dest, value);
                break; // 找到匹配 setter 即跳出內層循環
            }
        }
        return dest;
    }
}

測試用例:User Bean

public class User {
    private String name;
    private String id;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    public static void main(String[] args) throws Exception {
        User u1 = new User();
        u1.setName("zxb");
        u1.setId("3286");

        User u2 = new User();
        u2 = (User) BeanUtil2.copyBeanToBean(u1, u2);

        System.out.println(u2.getName()); // 輸出: zxb
        System.out.println(u2.getId());   // 輸出: 3286
    }
}

關鍵點與生產級擴展建議

  • 訪問權限:僅處理 public getter/setter;私有屬性需用 getDeclaredMethods() + setAccessible(true)。
  • 類型兼容:直接調用 setter 傳入 getter 返回值,若類型不兼容 (如 Integer → String) 會拋 IllegalArgumentException。生產級工具需引入 類型轉換器 (Converter)。
  • 只讀/只寫屬性:僅有 getter 無 setter 的屬性自動跳過;僅有 setter 無 getter 則無法從源讀取。
  • 集合/數組屬性:淺拷貝引用,若需深拷貝需遞歸處理或序列化反序列化。
  • 性能優化:反射調用較慢,高頻場景建議緩存 Method 對象或使用字節碼生成庫 (如 ASM、ByteBuddy) 生成拷貝類。
  • 推薦生產庫:org.apache.commons:commons-beanutils、org.springframework:spring-beans (BeanWrapper)、com.github.dozermapper:dozer (深拷貝/映射)、org.modelmapper:modelmapper。

掌握反射拷貝原理,既能在無第三方依賴環境快速實現需求,又能在引入成熟庫時理解其行為邊界,做到知其然更知其所以然。

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