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看效果如何!