在开发中经常需要将PO、VO、DTO、DO相互转换,如果一个个set将十分麻烦,现在也有很成熟的转换工具类,例如dozer,本人日常开发也会使用这个工具。出于技痒,于是自己开始研究利用反射实现转换工具。
2020/3/10版:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 利用反射原理的对象转换类
*/
public class BeanUtils {
private static final String GET = "get";
private static final String SET = "set";
/**
* 浅复制
*
* @param from 转换对象
* @param to 目标对象
* @param <T> 返回对象
* @return
*/
public static <T> T covert(T from, T to) {
System.out.println("-----------开始转换----------");
Class<?> fromClass = from.getClass();
Class<?> toClass = to.getClass();
Method[] toClassMethods = toClass.getMethods();
//遍历to含有的方法
for (Method method : toClassMethods) {
String methodName = method.getName();
// 如果该方法是set方法
if (methodName.startsWith("set")) {
try {
// 从from 获取对应的get方法
Method getMethod = fromClass.getDeclaredMethod(methodName.replace("set", "get"));
// 执行get方法获取from的值
Object value = getMethod.invoke(from);
// 执行set方法设置to的值
method.invoke(to, value);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
System.out.println("-----------转换完成----------");
return to;
}
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
UserEntity userEntity = new UserEntity();
userEntity.setId(1);
userEntity.setName("张三");
userEntity.setPassword("123admin");
userEntity.setSex("男");
userEntity.setUsername("user");
userEntity.setList(list);
System.out.println(userEntity);
UserVO covert = (UserVO) BeanUtils.covert(userEntity, new UserVO());
System.out.println(covert);
list.add(4);
System.out.println("------------修改原本的list后---------");
System.out.println(covert);
}
输出:
这个版本是我的第一版,目前实现了属性的浅复制,可以看到例子中list如果后面修改,会影响到转换后vo的list属性,原因是只实现了浅复制。本来想利用clone克隆实现深复制的。但是发现 Object value= getMethod.invoke(from); 我想不出如何调用将value强转回原本类型,唉还是对反射机制不够了解....待了解后在进行进一步补充。但是日常开发中大多数不会出现转换之后还需要修改原本pojo属性的场景。
2020/3/25
这里采用了取巧的方式,直接将原本的对象先深克隆,就不需要一步步判断是否需要对当前值进行克隆。
深克隆的方法有两种:1.序列化与反序列化生成对象。2.所有引用类型都得实现Cloneable。
这次采用了序列化与反序列化的思路,实现了对象的深克隆。(因为序列化的话比较现实,所有的引用类型都得实现Cloneable就太麻烦了)
//实现序列化接口
public class UserEntity implements Serializable {
private Integer id;
private String username;
private String password;
private String name;
private String sex;
private List<Integer> list;
}
/**
* 深拷贝
*
* @param from
* @param to
* @param <T>
* @return
*/
public static <T> T depthCovert(T from, T to) {
Class<?> fromClass = from.getClass();
Class<?> toClass = to.getClass();
System.out.println("-----------开始转换----------");
System.out.println("被转换的对象类型" + fromClass);
System.out.println("待转换的对象类型" + toClass);
if (!(fromClass instanceof Serializable)) {
throw new RuntimeException("转换对象没有实现Serializable");
}
try {
from = deepClone(from);
} catch (Exception e) {
throw new RuntimeException("深拷贝对象异常");
}
Method[] toClassMethods = toClass.getMethods();
//遍历to含有的方法
for (Method method : toClassMethods) {
String methodName = method.getName();
// 如果该方法是set方法
if (methodName.startsWith("set")) {
try {
// 从from 获取对应的get方法
Method getMethod = fromClass.getDeclaredMethod(methodName.replace("set", "get"));
// 执行get方法获取from的值
Object invoke = getMethod.invoke(from);
// 执行set方法设置to的值
method.invoke(to, invoke);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
System.out.println("-----------转换完成----------");
return to;
}
private static <T> T deepClone(T t) throws IOException, ClassNotFoundException {
System.out.println("-----------开始克隆----------");
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(t);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
t = (T) ois.readObject();
System.out.println("-----------结束克隆----------");
return t;
}
可以看到,userVO里的list和原本的list已经不是同一个List了,深拷贝成功。