Web 開發中常需將請求參數(本質是 Map<String, Object>)轉換為領域模型物件。手工 setXxx(map.get("xxx")) 既繁瑣又易出錯。Java 標準庫 java.beans.Introspector 提供了元編程能力,可自動分析 JavaBean 屬性並完成映射,實現零樣板碼轉換。
Introspector.getBeanInfo(Class):獲取類的 BeanInfo,包含所有屬性描述符PropertyDescriptor:描述單個屬性的讀寫方法(getReadMethod()/getWriteMethod())Method.invoke(obj, args):反射調用 setter 注入值public class MapToBeanConverter {
/**
* 將 Map 數據轉換為指定類型的 JavaBean 實例
*
* @param type 目標 Bean 類型
* @param map 包含屬性鍵值對的 Map
* @param <T> 泛型類型參數
* @return 填充好屬性的 Bean 實例
*/
public static <T> T convertMap(Class<T> type, Map<String, Object> map)
throws IntrospectionException, IllegalAccessException,
InstantiationException, InvocationTargetException {
// 1. 獲取 Bean 元數據
BeanInfo beanInfo = Introspector.getBeanInfo(type);
// 2. 實例化目標對象(需無參構造器)
T bean = type.getDeclaredConstructor().newInstance();
// 3. 遍歷屬性描述符,匹配 Map 鍵名並注入
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
String propName = pd.getName();
if (map.containsKey(propName)) {
Method writer = pd.getWriteMethod();
if (writer != null) {
try {
writer.invoke(bean, map.get(propName));
} catch (Exception e) {
// 單屬性失敗不影響其他屬性,記錄日誌即可
log.warn("屬性 {} 賦值失敗: {}", propName, e.getMessage());
}
}
}
}
return bean;
}
}
// 定義標準 JavaBean
class Person {
private String name;
private int age;
// getter/setter 省略...
}
// 測試轉換
Map<String, Object> data = new HashMap<>();
data.put("name", "張三");
data.put("age", 30);
Person person = MapToBeanConverter.convertMap(Person.class, data);
System.out.println(person.getName() + ", " + person.getAge()); // 輸出:張三, 30
Map 值為 Object,若類型不匹配(如字串塞入 int),反射調用會拋出 IllegalArgumentException。生產環境建議接入類型轉換器(如 Spring ConversionService)或校驗框架(Bean Validation)。
可預處理 Map 鍵名(轉小寫、下劃線轉駝峰)或維護字段別名映射表,提升兼容性。
反射調用有開銷,高頻場景可緩存 PropertyDescriptor[] 或使用字節碼生成庫(如 MapStruct、ModelMapper)編譯期生成優化代碼。
Introspector 是 Java 標準庫中被低估的寶藏,配合反射即可構建強大的 Map-to-Bean 映射器。理解其原理,不僅能快速解決參數綁定問題,更能深入掌握 JavaBean 規範與元編程精髓。