JAVA对象拷贝之Apache BeanUtils, Spring的BeanUtils的区别和源码分析(浅拷贝)

131 阅读3分钟

1.浅拷贝和深拷贝的区别

1.1浅拷贝
对于对象中的基本类型来说,浅拷贝进行拷贝的时候,进行的是值传递,而对引用类型的进行拷贝的时候,进行拷贝的仅仅是引用类型的引用地址而已,并没有进行新建一个对象,并将被拷贝对象中的成员变量拷贝过去。

1.2深拷贝
深拷贝较于浅拷贝的区别就是,深拷贝进行拷贝引用类型数据的时候,新建了一个对象,并将被拷贝对象的成员变量的值复制到了新对象里。

2.ApacheBeanUtils和Spring的BeanUtils的源码分析

2.1ApacheBeanUtils
ApacheBeanUtil的copyProperties先进性判断了拷贝对象和被拷贝对象是否为空值,又进行了判断对象是否实现了DynaBean接口,实现了该接口的还要进行复杂的类型转换,没有实现该接口的又进行判断是否实现了Map接口,同样如果实现了Map接口也要进行复杂的类型转换,如果没有实现DynaBean/Map就进入最后一个分支语句,在三中情况里,两种对JAVA自封装的对象进行拷贝的时候,都使用了反射,判断属性的可读性,可写性,综合下来,使用了判断空指针,类型转换,反射等,这一套下来,十分消耗性能,如果进行大量的对象拷贝可能会消耗大量的时间。

 public void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
    if (dest == null) {
        throw new IllegalArgumentException("No destination bean specified");
    } else if (orig == null) {
        throw new IllegalArgumentException("No origin bean specified");
    } else {
        if (this.log.isDebugEnabled()) {
            this.log.debug("BeanUtils.copyProperties(" + dest + ", " + orig + ")");
        }

        int var5;
        int var6;
        String name;
        Object value;
        if (orig instanceof DynaBean) {
            DynaProperty[] origDescriptors = ((DynaBean)orig).getDynaClass().getDynaProperties();
            DynaProperty[] var4 = origDescriptors;
            var5 = origDescriptors.length;

            for(var6 = 0; var6 < var5; ++var6) {
                DynaProperty origDescriptor = var4[var6];
                name = origDescriptor.getName();
                if (this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {
                    value = ((DynaBean)orig).get(name);
                    this.copyProperty(dest, name, value);
                }
            }
        } else if (orig instanceof Map) {
            Map<String, Object> propMap = (Map)orig;
            Iterator var13 = propMap.entrySet().iterator();

            while(var13.hasNext()) {
                Map.Entry<String, Object> entry = (Map.Entry)var13.next();
                String name = (String)entry.getKey();
                if (this.getPropertyUtils().isWriteable(dest, name)) {
                    this.copyProperty(dest, name, entry.getValue());
                }
            }
        } else {
            PropertyDescriptor[] origDescriptors = this.getPropertyUtils().getPropertyDescriptors(orig);
            PropertyDescriptor[] var14 = origDescriptors;
            var5 = origDescriptors.length;

            for(var6 = 0; var6 < var5; ++var6) {
                PropertyDescriptor origDescriptor = var14[var6];
                name = origDescriptor.getName();
                if (!"class".equals(name) && this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {
                    try {
                        value = this.getPropertyUtils().getSimpleProperty(orig, name);
                        this.copyProperty(dest, name, value);
                    } catch (NoSuchMethodException var10) {
                    }
                }
            }
        }

    }
}

2.2SpringBean源码的分析
进行读取SpringBean的源码后发现,先是判断了被拷贝对象是否可以编辑,如果可以编辑就获得可编辑对象,获取到背靠背对象的全部属性,并且还可以进行判断那些属性是被忽略不进行拷贝的,在循环拷贝中,也仅仅是尽心判断了对象的可读性和可写性,属性的可读性和可写性等,并没有进行大量的类型转换,实例判断等,这样就在大量对象进行拷贝的时候,节约了性能,提高了加载速度等。

private static void copyProperties(Object source, Object target, @Nullable Class<?> editable, @Nullable 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;
    PropertyDescriptor[] var7 = targetPds;
    int var8 = targetPds.length;

    for(int var9 = 0; var9 < var8; ++var9) {
        PropertyDescriptor targetPd = var7[var9];
        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) {
                    ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
                    ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
                    boolean isAssignable = !sourceResolvableType.hasUnresolvableGenerics() && !targetResolvableType.hasUnresolvableGenerics() ? targetResolvableType.isAssignableFrom(sourceResolvableType) : ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType());
                    if (isAssignable) {
                        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 var18) {
                            throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var18);
                        }
                    }
                }
            }
        }
    }

3.总结

3.1性能总结
Spring的BeanUtils优于Apache的BeanUtils.

3.2安全性总结
都很安全。

3.3阿里巴巴规约
阿里巴巴嵩山版JAVA规约中指出,不建议使用Apache的BeanUtils,建议使用Spring的BeanUtils或者BeanCopier、PropertieyUtils等进行对象的拷贝。 ————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:blog.csdn.net/2301_791087…