1. 程式人生 > 其它 >物件拷貝技術的對比總結

物件拷貝技術的對比總結

物件拷貝技術的對比總結

在業務編寫中,我們經常會有 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;
    }
}