物件拷貝技術的對比總結
阿新 • • 發佈:2022-03-31
物件拷貝技術的對比總結
在業務編寫中,我們經常會有 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; } }