对象拷贝技术的对比总结
对象拷贝技术的对比总结
在业务编写中,我们经常会有 DTO2VO,DTO2DO等场景,需要把一些属性从一个 bean里面拿出来,再设置到另一个 bean中。
技术上,我们可以自己手动一个个先 get 再 set,也可以选用第三方的一些拷贝工具,如:Spring的 BeanUtils,Apache的 BeanUtils, CGLIB的 BeanCopier,Dozer的 DozerBeanMapper等等
网上有以上几种工具的性能对比:链接
这里就不再复述。
总之,一般情况下,如果使用深拷贝,我们优先使用 DozerBeanMapper;
如果使用浅拷贝,我们可以使用 BeanCopier它的性能最高,但是一般需要自己再封装一层 api,也可以直接使用 Spring的 BeanUtils
DozerBeanMapper
DozerBeanMapper底层是将对象进行递归的拷贝,它支持三种映射方式:API,XML,注解。其实注解也包括在 API里面,它的使用可以让属性名不同的参数之间也可以直接拷贝。
Maven依赖:
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>5.5.1</version>
</dependency>
自己封装 api
public class BeanMapperUtils {
/**
* 持有Dozer单例, 避免重复创建DozerMapper消耗资源.
*/
public final static DozerBeanMapper dozer = new DozerBeanMapper();
/**
* 传入类对象实现转换后返回
*/
public static <T> T map(Object source, Class<T> destinationClass) {
if (source == null) {
return null;
}
return dozer.map(source, destinationClass);
}
/**
* 直接将值拷贝进入业务对象
*/
public static void map(Object source, Object destination) {
dozer.map(source, destination);
}
/**
* 转换集合中的对象
* 这个会很慢。。。
*/
public static <T> List<T> mapList(Collection sourceList, Class<T> destinationClass) {
List<T> destinationList = new ArrayList<>();
for (Object sourceObject : sourceList) {
T dest = dozer.map(sourceObject, destinationClass);
destinationList.add(dest);
}
return destinationList;
}
}
BeanCopier
BeanCopier是浅拷贝,需要导入 CGLIB的依赖。
并且如果两个属性类型不同(Integer和int的区别),那么拷贝也会失败,除非自定义一个类实现 Converter接口。
如果使用了 Converter,那么所有属性的拷贝都依赖 Converter,也就是说,这个 Converter里面必须把所有属性的转换都描述一遍,否则其他本来可以转换的属性也会转换失败。
public class BeanCopierUtil {
/**
* 缓存copier对象实例
*/
private static final Map<String, BeanCopier> CACHE_COPIER = new ConcurrentHashMap<>();
/**
* 直接传入两个对象进行拷贝
*/
public static <M, T> void copyProperties(M source, T target) {
if (source == null || target == null) {
throw new IllegalArgumentException();
}
BeanCopier copier = getBeanCopier(source.getClass(), target.getClass());
copier.copy(source, target, null);
}
/**
* 传入一个对象和一个类进行拷贝
*/
public static <T> T copyProperties(Object source, Class<T> targetClass) {
T t = null;
try {
t = targetClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
copyProperties(source, t);
return t;
}
/**
* 直接对象集合拷贝
*/
public static <T> List<T> copyProperties(Collection<?> sources, Class<T> target) {
if (sources == null || sources.isEmpty() || target == null) {
throw new IllegalArgumentException();
}
List<T> res = new ArrayList<>();
for (Object o : sources) {
T t = null;
try {
t = target.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
copyProperties(o, t);
res.add(t);
}
return res;
}
/**
* 获取 BeanCopier
*/
private static BeanCopier getBeanCopier(Class<?> sourceClass, Class<?> targetClass) {
String key = generateKey(sourceClass, targetClass);
if (CACHE_COPIER.containsKey(key)) {
return CACHE_COPIER.get(key);
}
BeanCopier copier = BeanCopier.create(sourceClass, targetClass, false);
CACHE_COPIER.put(key, copier);
return copier;
}
/**
* 生成缓存 BeanCopier的 key
*/
private static String generateKey(Class<?> sourceClass, Class<?> targetClass) {
String key = sourceClass.getName() + "_" + targetClass.getName();
return key;
}
}