对象拷贝技术的对比总结

对象拷贝技术的对比总结

在业务编写中,我们经常会有 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;
    }
}
posted @ 2022-03-31 11:37  小么VinVin  阅读(141)  评论(0编辑  收藏  举报