本文共 6564 字,大约阅读时间需要 21 分钟。
spring包中BeanUtils.copyProperties的实现。
spring中实现的方式很简单,就是对两个对象中相同名字的属性进行简单get/set,仅检查属性的可访问性。
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); ListignoreList = (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); } } } } } }
这种简单的方式注定了程序相比ApacheBeanUtils方法的高效性,但是会带来一些问题:null的Integer写入int类型时会报错!
User user = new User();User2 user2 = new User2();//设置空user.setId(null);//调用时会报错BeanUtils.copyProperties(user,user2);
输出结果如下:
Exception in thread "main" org.springframework.beans.FatalBeanException: Could not copy property 'id' from source to target; nested exception is java.lang.IllegalArgumentException at org.springframework.beans.BeanUtils.copyProperties(BeanUtils.java:626) at org.springframework.beans.BeanUtils.copyProperties(BeanUtils.java:538) at BeanUtilsTest.main(BeanUtilsTest.java:26)Caused by: java.lang.IllegalArgumentException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.beans.BeanUtils.copyProperties(BeanUtils.java:622) ... 2 more
Apache commons-beanutils
commons-beanutils则施加了很多的检验,包括类型的转换,甚至于还会检验对象所属的类的可访问性。默认情况下会将Ineger、Boolean、Long等基本类型包装类为null时的值复制后转换成0或者false,有时这个可能会引起不必要的麻烦。
public void copyProperties(final Object dest, final 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) { final DynaProperty[] origDescriptors = ((DynaBean) orig).getDynaClass().getDynaProperties(); for (DynaProperty origDescriptor : origDescriptors) { final String name = origDescriptor.getName(); // Need to check isReadable() for WrapDynaBean // (see Jira issue# BEANUTILS-61) if (getPropertyUtils().isReadable(orig, name) && getPropertyUtils().isWriteable(dest, name)) { final Object value = ((DynaBean) orig).get(name); copyProperty(dest, name, value); } } } else if (orig instanceof Map) { @SuppressWarnings("unchecked") final // Map properties are always of typeMap propMap = (Map ) orig; for (final Map.Entry entry : propMap.entrySet()) { final String name = entry.getKey(); if (getPropertyUtils().isWriteable(dest, name)) { copyProperty(dest, name, entry.getValue()); } } } else /* if (orig is a standard JavaBean) */ { final PropertyDescriptor[] origDescriptors = getPropertyUtils().getPropertyDescriptors(orig); for (PropertyDescriptor origDescriptor : origDescriptors) { final String name = origDescriptor.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 { final Object value = getPropertyUtils().getSimpleProperty(orig, name); /**检查类型等操作均在此函数 *@See org.apache.commons.beanutils.BeanUtilsBean#copyProperty */ copyProperty(dest, name, value); } catch (final 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。然后注册进去。所以在使用时
感觉commons-beanutils包中的这个BeanUtils类的copyProperties方法,太过复杂,约束太多,而且使用不便,虽然可扩展性好了,但是易用性不高。所以在我测试的时候阿里巴巴规约插件直接提示我不要使用。。。
总结:
关于bean复制,如果属性较少,建议直接写个方法完成get/set即可。如果属性较多,可以自己采用反射实现一个满足自己需要的工具类,或者使用spring的那个beanutils类,不建议使用commons-beanutils包中的那个BeanUtils类,刚看了下,这个类对于内部静态类的对象复制也会出现问题,检验太复杂了,常会出现一些诡异的问题。毕竟我们bean复制一般就是简单的属性copy而已。
转载地址:https://blog.csdn.net/h2453532874/article/details/96768694 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!