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;
}
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); // 統一初始化
}
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=[] (初始化)
| 特性 | 自研版 | Commons BeanUtils |
|---|---|---|
| 依賴 | 零依賴 | commons-beanutils + commons-logging |
| 空值初始化 | 內置支持 | 無 |
| 排除/包含字段 | 原生參數 | 需配合 PropertyUtils |
| 性能 | 緩存 Method 後可比肩 | 內部緩存較完善 |
生產環境建議:引入成熟庫 (Spring BeanUtils, Apache, MapStruct 編譯期生成);教學/輕量項目自研可控。