將任意 Java 物件的非空屬性轉為 Map<String, Object>,鍵為屬性名,值為屬性值。支援兩種介面:
Map<String, Object> objToMap(Object obj, Class<?> clazz);
Map<String, Object> objToMap(Object obj); // 自動獲取 obj.getClass()
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
public class BeanMapUtils {
/** 物件轉 Map,僅包含非 null 屬性 */
public static Map<String, Object> objToMap(Object obj) {
if (obj == null) return new HashMap<>();
return objToMap(obj, obj.getClass());
}
/** 指定類別轉 Map(可處理父類屬性) */
public static Map<String, Object> objToMap(Object obj, Class<?> clazz) {
Map<String, Object> map = new HashMap<>();
if (obj == null || clazz == null) return map;
// 遞迴處理父類鏈
Class<?> current = clazz;
while (current != null && current != Object.class) {
Field[] fields = current.getDeclaredFields();
for (Field field : fields) {
// 跳過 static、final、transient、synthetic 欄位
int mod = field.getModifiers();
if (Modifier.isStatic(mod) || Modifier.isFinal(mod)
|| Modifier.isTransient(mod) || Modifier.isVolatile(mod)) {
continue;
}
field.setAccessible(true); // 破壞封裝存取 private
try {
Object value = field.get(obj);
if (value != null) { // 核心:非空才放入
map.put(field.getName(), value);
}
} catch (IllegalAccessException e) {
// 忽略不可存取欄位
}
}
current = current.getSuperclass(); // 向上處理父類
}
return map;
}
}
public class User {
private String name = "Alice";
private Integer age = 25;
private String email = null; // 空值
private static String CONST = "IGNORED";
private transient String temp = "IGNORED";
}
// 測試
User u = new User();
Map<String, Object> map = BeanMapUtils.objToMap(u);
// 結果:{name=Alice, age=25} ← email、CONST、temp 皆被過濾
| 需求 | 實現方式 |
|---|---|
| 支援 Getter 方法(isXxx/getXxx) | 掃描 getMethods(),配合 Introspector |
| 自訂命名策略(駝峰/下劃線) | 引入 PropertyNamingStrategy (Jackson) |
| 日期/集合/巢狀物件序列化 | 遞迴呼叫或註冊 TypeAdapter |
| 效能優化(頻繁呼叫) | 快取 Field[] 陣列,使用 ConcurrentHashMap<Class, Field[]> |
| Spring 環境 | 直接用 BeanUtils.describe(obj) 或 MapStruct |
PropertyUtils.describe(obj),成熟穩定BeanUtils.describe(obj),Spring 專案零依賴objectMapper.convertValue(obj, Map.class),支援註解控制Java 反射實現物件轉 Map 完全可行。核心邏輯:getDeclaredFields() → setAccessible(true) → field.get(obj) → 非空判斷 → 放入 Map。生產環境建議優先使用成熟庫(Commons BeanUtils/Jackson/MapStruct),自寫工具類需注意效能快取、父類遞迴、特殊修飾符過濾等細節。