// 典型場景:ActionForm → Domain Object
user.setName(form.getName());
user.setId(form.getId());
user.setEmail(form.getEmail());
// ... 幾十行重複代碼
// 一行搞定
BeanUtils.copyProperties(dest, src);
ConvertUtils 自訂類型轉換器PropertyDescriptor,效能優於手寫反射InvocationTargetException、IllegalAccessExceptionpublic class BeanCopier {
public static void copy(Object src, Object dest) throws Exception {
Method[] srcMethods = src.getClass().getMethods();
Method[] destMethods = dest.getClass().getMethods();
Map destSetters = new HashMap<>();
for (Method m : destMethods) {
if (m.getName().startsWith("set") && m.getParameterCount() == 1) {
String prop = m.getName().substring(3);
destSetters.put(prop, m);
}
}
for (Method m : srcMethods) {
if (m.getName().startsWith("get") && m.getParameterCount() == 0) {
String prop = m.getName().substring(3);
Method setter = destSetters.get(prop);
if (setter != null) {
Object value = m.invoke(src);
setter.invoke(dest, value);
}
}
}
}
}
| 問題 | 說明 | 對策 |
|---|---|---|
| Boolean 存取器 | 可能是 isXxx() 而非 getXxx() | 同時檢查 get/is 前綴 |
| 不可讀/寫屬性 | 只有 getter 或只有 setter | 雙向存在時才拷貝 |
| 類型不匹配 | src.getXxx() 返回類型與 dest.setXxx() 參數不兼容 | 加入類型轉換邏輯或拋出明確異常 |
| 私有/繼承方法 | getMethods() 含繼承公開方法;getDeclaredMethods() 含私有 | 通常用 getMethods() 符合 JavaBean 規範 |
| 效能 | 反射慢於直接調用 | 快取 Method 物件、或用 MethodHandle/字節碼生成 |
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserDto toDto(UserEntity entity);
// 編譯期生成實現:直接調用 getter/setter,零反射
}
public class User {
private String name, id;
// getter/setter 省略
public static void main(String[] args) throws Exception {
User u1 = new User(); u1.setName("zxb"); u1.setId("3286");
User u2 = new User();
BeanCopier.copy(u1, u2);
System.out.println(u2.getName()); // zxb
System.out.println(u2.getId()); // 3286
}
}
BeanUtils.copyProperties)JavaBean 屬性拷貝是企業級開發高頻需求。理解反射機制、PropertyDescriptor、類型轉換體系,能讓你在選型、排查、優化時遊刃有餘。現代專案首推 MapStruct:編譯期安全、運行極致效能、IDE 友好重構支援。