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

JavaBean 與 Map 互轉工具類:內省機制與 Apache BeanUtils 兩種實現對比

     編輯:Java 綜合教學

JavaBean ↔ Map 雙向轉換完整方案

在參數傳遞、表單綁定、緩存序列化等場景中,JavaBean 與 Map 互轉是高頻需求。本文提供基於Java 內省與 Apache Commons BeanUtils 兩套生產級實現,並分析優劣。

方案一:Java 內省機制(零依賴,JDK 原生)

import java.beans.*;
import java.lang.reflect.Method;
import java.util.*;

public class BeanMapConverter {

    /** JavaBean → Map:過濾 class 屬性,只讀取可讀屬性 */
    public static Map<String, Object> beanToMap(Object bean) {
        if (bean == null) return Collections.emptyMap();
        Map<String, Object> map = new HashMap<>();
        try {
            BeanInfo info = Introspector.getBeanInfo(bean.getClass());
            for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
                String key = pd.getName();
                if (!"class".equals(key)) { // 過濾 Object.class 產生的 class 屬性
                    Method getter = pd.getReadMethod();
                    if (getter != null) {
                        map.put(key, getter.invoke(bean));
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Bean轉Map失敗: " + bean.getClass(), e);
        }
        return map;
    }

    /** Map → JavaBean:只設置可寫屬性,類型自動轉換 */
    public static void mapToBean(Map<String, Object> map, Object bean) {
        if (map == null || bean == null) return;
        try {
            BeanInfo info = Introspector.getBeanInfo(bean.getClass());
            for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
                String key = pd.getName();
                if (map.containsKey(key)) {
                    Method setter = pd.getWriteMethod();
                    if (setter != null) {
                        Object value = map.get(key);
                        // 簡單類型轉換:String → 基本類型/包裝類
                        setter.invoke(bean, convertType(value, pd.getPropertyType()));
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Map轉Bean失敗: " + bean.getClass(), e);
        }
    }

    /** 基礎類型轉換輔助 */
    private static Object convertType(Object value, Class<?> targetType) {
        if (value == null) return null;
        if (targetType.isAssignableFrom(value.getClass())) return value;
        String str = value.toString();
        if (targetType == int.class || targetType == Integer.class) return Integer.parseInt(str);
        if (targetType == long.class || targetType == Long.class) return Long.parseLong(str);
        if (targetType == double.class || targetType == Double.class) return Double.parseDouble(str);
        if (targetType == boolean.class || targetType == Boolean.class) return Boolean.parseBoolean(str);
        // 可擴展:Date、Enum、BigDecimal 等
        return value;
    }
}

方案二:Apache Commons BeanUtils(一行代碼,功能豐富)

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
</dependency>

// Bean → Map
Map<String, Object> map = BeanUtils.describe(bean); // 含 class 鍵,需自行移除

// Map → Bean
BeanUtils.populate(bean, map); // 自動類型轉換、巢狀屬性、集合填充

深度對比評測

維度內省實現BeanUtils
依賴無commons-beanutils + commons-logging
性能較快(直接反射調用)稍慢(內部緩存 PropertyDescriptor,首次較慢)
類型轉換需手寫內建 ConvertUtils,支持自定義 Converter
巢狀屬性不支持支持 address.city 等 OGNL 風格
只讀/只寫屬性可精確控制自動跳過無 getter/setter
異常處理統一拋 RuntimeException拋 InvocationTargetException/IllegalAccessException

選型建議

  • 輕量級項目/Android/原生依賴敏感 → 內省實現
  • 企業級應用/複雜表單/巢狀對象 → BeanUtils
  • 高性能熱點路徑 → 考慮 MapStruct 編譯期生成代碼(零反射)

進階避坑指南

  1. Introspector.getBeanInfo() 會包含 Object 類的 class 屬性,必須過濾
  2. Map キー與屬性名大小寫必須一致(JavaBean 規範:駝峰命名)
  3. 集合/數組屬性轉換極其複雜,建議專用序列化庫
  4. BeanUtils 1.9.4+ 需顯式註冊 ConvertUtils.register() 自定義類型轉換器

掌握這兩套方案,可覆蓋 95% 的 Bean-Map 轉換場景。核心原理皆基於 PropertyDescriptor 讀寫方法元數據,理解內省機制有助於排查疑難雜癥。

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