1. 程式人生 > >Java 開發中的對象拷貝

Java 開發中的對象拷貝

通過反射 log exc see void 沒有 ima 對比 tde

前言

在 Java 開發中,很多時候需要將兩個屬性基本相同的對象進行屬性復制,比如 DO 轉 VO等等。

本文主要介紹自己實現的簡易拷貝工具類與 Spring 提供的屬性拷貝的對比。

Spring 提供的屬性拷貝

在 Spring 中直接調用 BeanUtils.copyProperties();即可。

它的核心通過循環 target 的所有方法名,然後在 source 中找到對應的方法名,最後通過反射從 source 中獲取並寫入 target 中。

Spring 沒有通過 java.lang.reflect 中的 Field 來做,而是通過 java.beans 中的 PropertyDescriptor 來實現。

  補充:PropertyDescriptor 描述 Java Bean 通過一對存儲器方法導出的一個屬性。

通過 PropertyDescriptor 提供的 getReadMethod() 和 getWriteMethod() 方法,可以方便的獲取到讀取、寫入屬性值的方法(Method)。

同時,Spring 也做了緩存,在測試中,第一次的對象拷貝用時 300+ 毫秒,之後在緩存中獲取,用時 0 毫秒。

源碼如下圖所示:

技術分享

技術分享

緩存源碼:

技術分享

自己寫的簡易版

 1 public static void copyBean(Object target, Object source) throws
IllegalAccessException, InstantiationException, NoSuchFieldException { 2 Class targetClass = target.getClass(); 3 Class sourceClass = source.getClass(); 4 // 獲取目標類的所有參數 Field 5 Field[] fields = targetClass.getDeclaredFields(); 6 // 為目標類的每個參數設值 7 for
(Field field : fields) { 8 // 如果數據源對象中存在對應的參數 9 Field sourceField = sourceClass.getDeclaredField(field.getName()); 10 if (null != sourceField) { 11 Field targetField = targetClass.getDeclaredField(field.getName()); 12 targetField.setAccessible(true); 13 sourceField.setAccessible(true); 14 targetField.set(target, sourceField.get(source)); 15 } 16 } 17 } 18 19 // 類似 Spring 的版本 20 public static void copyBeanByMethod(Object target, Object source) throws IntrospectionException, InvocationTargetException, IllegalAccessException { 21 Class targetClass = target.getClass(); 22 Class sourceClass = source.getClass(); 23 Field[] sourceFields = sourceClass.getDeclaredFields(); 24 for (Field field : sourceFields) { 25 PropertyDescriptor targetProperty; 26 try { 27 targetProperty = new PropertyDescriptor(field.getName(), targetClass); 28 } catch (IntrospectionException e) { 29 continue; 30 } 31 Method writeMethod = targetProperty.getWriteMethod(); 32 if (writeMethod != null) { 33 PropertyDescriptor sourceProperty = new PropertyDescriptor(field.getName(), sourceClass); 34 Method readMethod = sourceProperty.getReadMethod(); 35 if (!Modifier.isPublic(readMethod.getModifiers())) { 36 readMethod.setAccessible(true); 37 } 38 // 讀取 source 中屬性的值 39 Object value = readMethod.invoke(source); 40 if (!Modifier.isPublic(writeMethod.getModifiers())) { 41 writeMethod.setAccessible(true); 42 } 43 // 為 target 對應的屬性賦值 44 writeMethod.invoke(target, value); 45 } 46 } 47 }

小結

Spring 所提供的屬性拷貝雖然第一次效率較低,但隨後如果再次使用相同的 source 進行拷貝,則 Spring 會通過第一次拷貝保存的緩存來直接進行快速的拷貝

參考資料

[1] 談談 Java 開發中的對象拷貝

Java 開發中的對象拷貝