接口开发中BeanUtils.copyProperties的使用

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/xiao_dondon/article/details/79761284

在后台开发过程中我们肯定会使用各种各样的bean,我目前遇到2类

其一是dto:返回给调用方的bean

另外是entity:与数据库映射的bean

由此就会遇到bean直接赋值的问题,我们常常使用BeanUtils.copyProperties(a,b)来进行赋值,将a中的属性赋值给b中的属性(浅复制)


在最近的接口开发过程中遇到的场景:

前端传过来的数据包装成一个dto(a),然后new一个对应的entity(b),利用BeanUtils.copyProperties(a,b),将a中的数据传输给b,之后将b存储在数据库当中,这是非常简单的

我们再来看另外一个场景:

通常,a中的属性值会比b中少,因为,我们很多情况下不会把b中的所有数据都暴露给前端,这对于数据安全是十分重要的,也就是说,同样一个bean,a中的某个属性为null并不代表b中的该属性也是null,很有可能是有值的。

如果前端拿到一个dto,并且修改了其中的某个属性值,如果直接使用BeanUtils.copyProperties(a,b)来进行bean的相互赋值,就会造成b中的数据损失,持久层数据异常。

举例:

b{
user:job
password:123
age:29
}

前端拿到该bean,password属性被屏蔽,然后修改了年龄

a{
user:job
password:null
age:22
}

此时,我们需要将修改的年龄同步到数据库,如果直接使用BeanUtils.copyProperties(a,b)的话

b{
user:job
password:null
age:22
}

可以看到,数据库中b的密码已经为空了,这样你可就惨了~


那么,BeanUtils.copyProperties内部是如何实现这样的赋值功能的呢?我们看一下源码(a等同于source,b等同于target):

        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 && 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 var15) {
                            throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var15);
                        }
                    }
                }
            }
        }

PropertyDescriptor类表示JavaBean类通过存储器导出一个属性。主要方法:
      1.  getReadMethod(),获得用于读取属性值的方法

      2.  getWriteMethod(),获得用于写入属性值的方法

我们可以看到targetPds是一个PropertyDescriptor数组,长度就等于b的属性数目。

先循环,判断a中sourcePd是否为空(注意,不论a中该属性的值是否为空,只要有该属性就不为空)

然后把a中该属性的值赋给value,注意此时并没有判断value是否为空,所以就造成了上面我们所说持久层数据损失的情况了。



猜你喜欢

转载自blog.csdn.net/xiao_dondon/article/details/79761284