java bean拷貝操做又一個很是好用的工具類 BeanUitls :
spring (org.springframework.beans.BeanUtils)和apache commons-beanutils(org.apache.commons.beanutils.BeanUtils)中分別存在一個BeanUtils,提供了對。
特別注意 這兩個類在不一樣的包下面,而這兩個類的copyProperties()方法裏面傳遞的參數賦值是相反的。
例如:
a,b爲對象
BeanUtils.copyProperties(a, b);java
BeanUtils是org.springframework.beans.BeanUtils,
a拷貝到b
BeanUtils是org.apache.commons.beanutils.BeanUtils,
b拷貝到aspring
以前在寫程序時,用到了兩個不一樣類型但屬性基本相同的對象的拷貝,因爲類型不一樣源bean裏屬性(Integer 向 int 拷貝)其值爲null,這時會拋異常。sql
/** * 測試 spring 中的 bean 拷貝 * @throws Exception */ @org.junit.Test public void testBeanCopy() throws Exception { PersonEntity pe = new PersonEntity(); pe.setAge(1); //pe.setId(1234); // id 爲 Integer 此時爲null pe.setName("kevin"); Person person = new Person(); // Person 中的id爲int類型 BeanUtils.copyProperties(pe, person); System.out.println(person); }
一個看似簡單的問題困擾了在當時,因而抽空看了一下spring和apache commons-beanutils包中BeanUtils.copyProperties的實現。apache
spring中實現的方式很簡單,就是對兩個對象中相同名字的屬性進行簡單get/set,僅檢查屬性的可訪問性。數組
源碼app
package org.springframework.beans; private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties) throws BeansException { Assert.notNull(source, "Source must not be null"); Assert.notNull(target, "Target must not be null"); Class<?> actualEditable = target.getClass(); if (editable != null) { if (!editable.isInstance(target)) { throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]"); } actualEditable = editable; } PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null); for (PropertyDescriptor targetPd : targetPds) { Method writeMethod = targetPd.getWriteMethod(); if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) { PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); if (sourcePd != null) { Method readMethod = sourcePd.getReadMethod(); if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) { try { if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { readMethod.setAccessible(true); } Object value = readMethod.invoke(source); if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { writeMethod.setAccessible(true); } writeMethod.invoke(target, value); } catch (Throwable ex) { throw new FatalBeanException( "Could not copy property '" + targetPd.getName() + "' from source to target", ex); } } } } } }
而commons-beanutils則施加了不少的檢驗,包括類型的轉換,甚至於還會檢驗對象所屬的類的可訪問性。工具
源碼測試
package org.apache.commons.beanutils; public void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException { // Validate existence of the specified beans if (dest == null) { throw new IllegalArgumentException ("No destination bean specified"); } if (orig == null) { throw new IllegalArgumentException("No origin bean specified"); } if (log.isDebugEnabled()) { log.debug("BeanUtils.copyProperties(" + dest + ", " + orig + ")"); } // Copy the properties, converting as necessary if (orig instanceof DynaBean) { DynaProperty origDescriptors[] = ((DynaBean) orig).getDynaClass().getDynaProperties(); for (int i = 0; i < origDescriptors.length; i++) { String name = origDescriptors[i].getName(); if (getPropertyUtils().isWriteable(dest, name)) { Object value = ((DynaBean) orig).get(name); copyProperty(dest, name, value); } } } else if (orig instanceof Map) { Iterator names = ((Map) orig).keySet().iterator(); while (names.hasNext()) { String name = (String) names.next(); if (getPropertyUtils().isWriteable(dest, name)) { Object value = ((Map) orig).get(name); copyProperty(dest, name, value); } } } else /* if (orig is a standard JavaBean) */ { PropertyDescriptor origDescriptors[] = getPropertyUtils().getPropertyDescriptors(orig); for (int i = 0; i < origDescriptors.length; i++) { String name = origDescriptors[i].getName(); if ("class".equals(name)) { continue; // No point in trying to set an object's class } if (getPropertyUtils().isReadable(orig, name) && getPropertyUtils().isWriteable(dest, name)) { try { Object value = getPropertyUtils().getSimpleProperty(orig, name); copyProperty(dest, name, value); } catch (NoSuchMethodException e) { ; // Should not happen } } } } }
並且,commons-beanutils中的裝換是不支持java.util.Date的。除了支持基本類型以及基本類型的數組以外,還支持java.sql.Date, java.sql.Time, java.sql.TimeStamp, java.io.File, javaio.URL這些類的對象,其他一律不支持。不過你能夠自定義你的類的Converter。而後註冊進去。因此在使用時spa
感受commons-beanutils包中的這個BeanUtils類的copyProperties方法,太過複雜,約束太多,並且使用不便,雖然可擴展性好了,可是易用性不高。debug
總結:
關於bean複製,若是屬性較少,建議直接寫個方法完成get/set便可。若是屬性較多,能夠本身採用反射實現一個知足本身須要的工具類,或者使用spring的那個beanutils類,不建議使用commons-beanutils包中的那個BeanUtils類,剛看了下,這個類對於內部靜態類的對象複製也會出現問題,檢驗太複雜了,常會出現一些詭異的問題。畢竟咱們bean複製通常就是簡單的屬性copy而已。
並且,因爲這些BeanUtils類都是採用反射機制實現的,對程序的效率也會有影響。所以,慎用BeanUtils.copyProperties!!!或者使用clone看效果如何!