BeanUtils 工具 copyProperties 拷贝对象

来源:互联网 发布:人工智能 库布里克 编辑:程序博客网 时间:2024/06/07 19:46

java bean拷贝操作又一个非常好用的工具类 BeanUitls :
spring (org.springframework.beans.BeanUtils)和apache commons-beanutils(org.apache.commons.beanutils.BeanUtils)中分别存在一个BeanUtils,提供了对。
特别注意 这两个类在不同的包下面,而这两个类的copyProperties()方法里面传递的参数赋值是相反的。
例如:
a,b为对象
BeanUtils.copyProperties(a, b);

BeanUtils是org.springframework.beans.BeanUtils,
a拷贝到b
BeanUtils是org.apache.commons.beanutils.BeanUtils,
b拷贝到a

之前在写程序时,用到了两个不同类型但属性基本相同的对象的拷贝,由于类型不同源bean里属性(Integer 向 int 拷贝)其值为null,这时会抛异常。

 /**     * 测试 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的实现。

spring中实现的方式很简单,就是对两个对象中相同名字的属性进行简单get/set,仅检查属性的可访问性。

源码

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。然后注册进去。所以在使用时

感觉commons-beanutils包中的这个BeanUtils类的copyProperties方法,太过复杂,约束太多,而且使用不便,虽然可扩展性好了,但是易用性不高。

总结:

关于bean复制,如果属性较少,建议直接写个方法完成get/set即可。如果属性较多,可以自己采用反射实现一个满足自己需要的工具类,或者使用spring的那个beanutils类,不建议使用commons-beanutils包中的那个BeanUtils类,刚看了下,这个类对于内部静态类的对象复制也会出现问题,检验太复杂了,常会出现一些诡异的问题。毕竟我们bean复制一般就是简单的属性copy而已。

而且,由于这些BeanUtils类都是采用反射机制实现的,对程序的效率也会有影响。因此,慎用BeanUtils.copyProperties!!!或者使用clone看效果如何!

原创粉丝点击