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

Java 反射實現 BeanUtils:屬性拷貝、排除/包含字段、空值初始化

     編輯:Java 相關知識

功能需求

  • 源對象 → 目標對象**同名同類型屬性拷貝**
  • 支持**排除字段** / **僅包含字段**
  • 拷貝後自動**初始化目標對象中為 null 的基本類型/集合字段** (避免 NPE、JSON 序列化缺失)

核心實現邏輯

1. Getter/Setter 識別


private static boolean isGetter(Method m) {
    Class<?> ret = m.getReturnType();
    if (ret == void.class) return false;
    String name = m.getName();
    return (name.startsWith("get") || name.startsWith("is")) && m.getParameterCount() == 0;
}
private static boolean isSetter(Method m) {
    return m.getName().startsWith("set") && m.getParameterCount() == 1;
}

2. 拷貝主流程 (排除模式)


public static void copyPropertiesExclude(Object src, Object target, String[] exclude) {
    Set<String> exSet = exclude != null ? new HashSet<>(Arrays.asList(exclude)) : Collections.emptySet();
    Method[] srcMethods = src.getClass().getDeclaredMethods();
    Method[] tgtMethods = target.getClass().getDeclaredMethods();

    for (Method sm : srcMethods) {
        if (!isGetter(sm)) continue;
        String prop = sm.getName().substring(3); // 去掉 get/is
        if (exSet.contains(prop)) continue;

        Method tm = findSetter(tgtMethods, prop);
        if (tm == null || !isSetter(tm)) continue;

        Object val = sm.invoke(src);
        if (val == null) continue;
        if (val instanceof Collection && ((Collection)val).isEmpty()) continue;

        tm.invoke(target, val);
    }
    initNullFields(target); // 統一初始化
}

3. 空值初始化策略


private static void initNullFields(Object obj) throws Exception {
    for (Field f : obj.getClass().getDeclaredFields()) {
        String getter = "get" + upperFirst(f.getName());
        Method gm = obj.getClass().getDeclaredMethod(getter);
        Object val = gm.invoke(obj);
        if (val != null) continue;

        String setter = "set" + upperFirst(f.getName());
        Method sm = obj.getClass().getDeclaredMethod(setter, f.getType());
        Class<?> type = f.getType();
        if (type == int.class || type == Integer.class) sm.invoke(obj, 0);
        else if (type == long.class || type == Long.class) sm.invoke(obj, 0L);
        else if (type == double.class || type == Double.class) sm.invoke(obj, 0.0);
        else if (type == String.class) sm.invoke(obj, "");
        else if (type.isAssignableFrom(List.class)) sm.invoke(obj, new ArrayList<>());
        else if (type.isAssignableFrom(Set.class)) sm.invoke(obj, new HashSet<>());
        else if (type.isAssignableFrom(Map.class)) sm.invoke(obj, new HashMap<>());
        // 其他類型可擴展
    }
}

使用示例


User src = new User();
src.setId(1L);
src.setName("Alice");
src.setTags(Arrays.asList("vip", "admin"));

UserDTO dto = new UserDTO();
BeanUtils.copyPropertiesExclude(src, dto, new String[]{"password"}); // 排除敏感字段
// dto: id=1, name=Alice, tags=[vip,admin], password=null→"", roles=[] (初始化)

對比 Apache Commons BeanUtils

特性自研版Commons BeanUtils
依賴零依賴commons-beanutils + commons-logging
空值初始化內置支持無
排除/包含字段原生參數需配合 PropertyUtils
性能緩存 Method 後可比肩內部緩存較完善

生產環境建議:引入成熟庫 (Spring BeanUtils, Apache, MapStruct 編譯期生成);教學/輕量項目自研可控。

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