在實際的工作中,有時可能存在2個java bean屬性之間的拷貝,而如果使用bean 之間setter方法進行設定,那麼将會存在大量的備援的代碼,是以可以考慮使用反射來進行屬性的拷貝操作。
大緻思路如下:
1、從class檔案中,擷取到所有的public類型的方法
2、擷取到所有的getter方法和setter方法,getter方法的擷取需要考慮到boolean類型這個比較特殊的類型的擷取。
3、進行屬性的拷貝的時候,需要考慮到源對象中的屬性值為null,是否應該拷貝到目标對象中
4、一個class檔案中的getter和setter方法一般都是不可變的,是以需要進行緩存起來,避免每次都進行擷取。
一、編寫BeanUtils工具類,實作拷貝
/**
* bean 之間的屬性的複制.
*
* @描述
* @作者 huan
* @時間 2017年11月4日 - 上午11:25:26
*/
public final class BeanUtils {
private static final ConcurrentHashMap<Class<?>, List<Method>> CACHE_CLASS_GET_METHOD = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Class<?>, List<Method>> CACHE_CLASS_SET_METHOD = new ConcurrentHashMap<>();
private BeanUtils() {
}
/**
* 将src java bean的屬性拷貝到 tar java bean的屬性中,預設不拷貝src對象中的空對象的值.
*
* @param src
* 源對象
* @param tar
* 目标對象
*/
public static void copyProperties(Object src, Object tar) {
copyProperties(src, tar, true);
}
/**
* 将src java bean的屬性拷貝到 tar java bean的屬性中
*
* @param src
* 源對象
* @param tar
* 目标對象
* @param skipEmpty
* 如果src中屬性的值時null,是否跳過這個拷貝, true:跳過 false:不跳過
*/
public static void copyProperties(Object src, Object tar, boolean skipEmpty) {
List<Method> getterMethods = getGetMethod(src.getClass());
List<Method> setterMethods = getSetMethod(tar.getClass());
try {
for (Method getMethod : getterMethods) {
Object value = getMethod.invoke(src);
if (skipEmpty && value == null) {
continue;
}
Method method = setterMethods.stream().filter(m -> Objects.equals(m.getName(), getInvokedSetterMethodName(getMethod.getName()))).findFirst().orElse(null);
if (null != method) {
method.invoke(tar, value);
}
}
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("bean複制屬性過程中産生異常", e);
}
}
/**
* 根據getter的方法名拿到setter的方法名
*
* @param getterMethodName
* getter的方法名
* @return setter的方法名
*/
public static String getInvokedSetterMethodName(String getterMethodName) {
if (getterMethodName.startsWith("get")) {
return "set" + getterMethodName.substring(3);
} else {
return "set" + getterMethodName.substring(2);
}
}
/**
* 擷取clazz中的所有public getter 方法
*
* @param clazz
* 需要擷取getter方法的類
* @return public getter方法
*/
public static List<Method> getGetMethod(Class<?> clazz) {
Class<?> lockClazz = clazz;
if (!CACHE_CLASS_GET_METHOD.containsKey(clazz)) {
synchronized (lockClazz) {
if (!CACHE_CLASS_GET_METHOD.containsKey(clazz)) {
CACHE_CLASS_GET_METHOD.put(clazz, Arrays.stream(getMethods(clazz)).filter(BeanUtils::isGetterMethod).filter(m -> !Objects.equals(m.getName(), "getClass")).collect(Collectors.toList()));
}
}
}
return CACHE_CLASS_GET_METHOD.get(clazz);
}
/**
* 擷取clazz中的所有public setter 方法
*
* @param clazz
* 需要擷取setter方法的類
* @return public setter方法
*/
public static List<Method> getSetMethod(Class<?> clazz) {
Class<?> lockClazz = clazz;
if (!CACHE_CLASS_SET_METHOD.containsKey(clazz)) {
synchronized (lockClazz) {
if (!CACHE_CLASS_SET_METHOD.containsKey(clazz)) {
CACHE_CLASS_SET_METHOD.put(clazz, Arrays.stream(getMethods(clazz)).filter(BeanUtils::isSetterMethod).collect(Collectors.toList()));
}
}
}
return CACHE_CLASS_SET_METHOD.get(clazz);
}
/**
* 擷取一個類中中的所有的方法
*
* @param clazz
* @return
*/
public static Method[] getMethods(Class<?> clazz) {
return clazz.getMethods();
}
/**
* 判斷一個方式是否是getter方法
*
* @param method
* 需要判斷的方法
* @return true:是getter方法 false:不是getter方法
*/
public static boolean isGetterMethod(Method method) {
if (method.getName().startsWith("get") && method.getParameterCount() == 0 && method.getReturnType() != Void.class) {
return true;
} else if (method.getName().startsWith("is") && method.getParameterCount() == 0 && (method.getReturnType().equals(boolean.class) || method.getReturnType().equals(Boolean.class))) {
return true;
}
return false;
}
/**
* 判斷是否是setter方法
*
* @param method
* 需要判斷的方法
* @return true是setter方法 false不是setter方法
*/
public static boolean isSetterMethod(Method method) {
boolean isSetter = false;
if (method.getName().startsWith("set") && method.getParameterCount() == 1 && method.getReturnType() != Void.class) {
isSetter = true;
}
return isSetter;
}
}
二、測試
1、編寫一個實體類 SysResource
/**
* 資源實體類
*
* @描述
* @作者 huan
* @時間 2017年11月4日 - 上午11:26:46
*/
@Data
public class SysResource {
private String id;
private String pid;
private String name;
private String url;
private Integer type;
private Date createTime;
}
2、編寫測試代碼
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5COxIjMyEDOhhDNwUDM1gTYyYzX2MTOwcTM0EzLcZDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)