背景

在开发过程中, 在数据库查询和接口调用过程中,为了隔离,我们往往会将查询出来的对象(包括数据库返回和接口返回)和对外提供的实体对象隔离开来。此时就需要把一个对象的属性拷贝到目标对象中
通常有2种做法:
1、一个一个set b.setField(a.getField());
2、使用拷贝的工具类,比方说 BeanUtils.copyProperties (因为他们的类结构和属性字段大多是类似的)

对于字段比较多的场景,使用BeanUtils明显更加简洁

那么问题来了,
BeanUtils对于对象中包含的对象也能够帮我们进行拷贝吗?
如果会进行拷贝,它帮我们做的拷贝是深拷贝还是浅拷贝?

深浅拷贝 的主要区别就是:复制的是引用还是复制的是值。

如果仅仅复制了引用,也就是说,复制之后,原来的变量和新的变量指向同一个地址,彼此之间的操作会互相影响,为 浅拷贝。
而如果是在堆中重新分配内存,拥有不同的地址,但是值是一样的,复制后的对象与原来的对象是完全隔离,互不影响,就是深拷贝。

BeansUtils 实战

创建两个类 Student和Course 以及一个目标类StudentEntity(和Student类除了名字以外其余字段和结构都完全一致)

public class Student {private String name;private Integer age;/*** 是否已毕业* */private Boolean isGraduated;private Course course;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Boolean getGraduated() {return isGraduated;}public void setGraduated(Boolean graduated) {isGraduated = graduated;}public Course getCourse() {return course;}public void setCourse(Course course) {this.course = course;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", isGraduated=" + isGraduated +", course=" + course +'}';}
public class Course {private String courseName;private Integer score;public String getCourseName() {return courseName;}public void setCourseName(String courseName) {this.courseName = courseName;}public Integer getScore() {return score;}public void setScore(Integer score) {this.score = score;}@Overridepublic String toString() {return "Course{" +"courseName='" + courseName + '\'' +", score=" + score +'}';}
}

测试类:

public class Test {@org.junit.Testpublic void testCopyStudent(){Student student = new Student();student.setName("chenpp");student.setAge(21);student.setGraduated(true);Course course = new Course();course.setCourseName("Chinese");course.setScore(100);student.setCourse(course);StudentEntity copyStudent = new StudentEntity();BeanUtils.copyProperties(student, copyStudent);System.out.println("拷贝后的对象" + copyStudent);copyStudent.getCourse().setCourseName("Math");System.out.println("修改拷贝后的course,原来的对象" + student);}
}


从上面的代码可以看出,spring-beans提供的BeanUtils.copyProperties对于外层的属性无论是基本数据类型还是引用数据类型都会进行拷贝,使用的是浅拷贝

Boolean和boolean类型的拷贝

在使用过程中,发现如果Student的isGraduated字段的set方法定义成isGraduated(),那么拷贝就会失败

public class Student {private String name;private Integer age;/*** 是否已毕业* */private Boolean isGraduated;private Course course;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Boolean isGraduated() {return isGraduated;}public void setIsGraduated(Boolean isGraduated) {this.isGraduated = isGraduated;}
}

测试结果: isGraduated字段没有被拷贝

跟踪下源码看看是什么原因?

private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties)throws BeansException {Class<?> actualEditable = target.getClass();//...//根据targetClass获取其属性描述器PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);//...for (PropertyDescriptor targetPd : targetPds) {Method writeMethod = targetPd.getWriteMethod();if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {//根据sourceClass获取其属性描述器(和targetClass执行的方法一样)PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());if (sourcePd != null) {//如果源对象的readMethod为空,就无法获取到源对象的值,也就无法通过反射进行赋值Method readMethod = sourcePd.getReadMethod();if (readMethod != null &&ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {try {//...writeMethod.invoke(target, value);}catch (Throwable ex) {}}}}}}

其源码路径如下
BeanUtils.getPropertyDescriptor -->
     CachedIntrospectionResults.forClass(clazz) -->
           new CachedIntrospectionResults(beanClass) -->
                Introspector.getBeanInfo(Class<?> beanClass) -->
                     Introspector.getBeanInfo()

private BeanInfo getBeanInfo() throws IntrospectionException {// the evaluation order here is import, as we evaluate the// event sets and locate PropertyChangeListeners before we// look for properties.BeanDescriptor bd = getTargetBeanDescriptor();MethodDescriptor mds[] = getTargetMethodInfo();EventSetDescriptor esds[] = getTargetEventInfo();//关键方法,获取目标对象的属性信息 这里就是StudentEntityPropertyDescriptor pds[] = getTargetPropertyInfo();int defaultEvent = getTargetDefaultEventIndex();int defaultProperty = getTargetDefaultPropertyIndex();return new GenericBeanInfo(bd, esds, defaultEvent, pds,defaultProperty, mds, explicitBeanInfo);}
  private PropertyDescriptor[] getTargetPropertyInfo() {Method methodList[] = getPublicDeclaredMethods(beanClass);for (int i = 0; i < methodList.length; i++) {//...Class<?>[] argTypes = method.getParameterTypes();Class<?> resultType = method.getReturnType();int argCount = argTypes.length;//...if (argCount == 0) {if (name.startsWith(GET_PREFIX)) {// Simple getterpd = new PropertyDescriptor(this.beanClass, name.substring(3), method, null);} else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) {// Boolean getterpd = new PropertyDescriptor(this.beanClass, name.substring(2), method, null);}}//...}}

从这里就可以看到,如果属性是Boolean类型,其read方法需要以getXXX命名,而对于boolean类型,需要以isXX命名

根据class获取对应的属性信息之后,会缓存在propertyDescriptorCache里,debug可以看到StudentEntity的isGraduated字段对应的readMethod为空(Student的isGraduated字段对应的readMethod也为空)

总结

BeanUtils.copyProperties主要是通过反射实现的浅拷贝,如果对象都是单一的属性或者子对象不涉及到改动,可以BeanUtils进行拷贝
在拷贝的时候注意对于Boolean类型字段,其readMethod需要以getXXX来命名

spring BeanUtils.copyProperties浅拷贝之特殊的Boolean相关推荐

  1. spring BeanUtils.copyProperties只拷贝不为null的属性

    在MVC的开发模式中经常需要将model与pojo的数据绑定,apache和spring的工具包中都有BeanUtils,使用其中的copyProperties方法可以非常方便的进行这些工作,但在实际 ...

  2. java对象复制到另一个对象中_spring: beanutils.copyproperties将一个对象的数据塞入到另一个对象中(合并对象)...

    spring: beanutils.copyproperties将一个对象的数据塞入到另一个对象中(合并对象) 它的出现原因: BeanUtils提供对Java反射和自省API的包装.其主要目的是利用 ...

  3. Java DO到DTO转换利用spring 的BeanUtils.copyProperties

    Java DO到DTO转换利用spring 的BeanUtils.copyProperties() public static <T> List<T> copy(Object ...

  4. 两难!到底用Apache BeanUtils还是Spring BeanUtils?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 在我们实际项目开发过程中,我们经常需要将不同的两个对象实例进行属性 ...

  5. 对象拷贝 Apache BeanUtils与Spring BeanUtils性能比较

    前言 在我们实际项目开发过程中,我们经常需要将不同的两个对象实例进行属性复制,从而基于源对象的属性信息进行后续操作,而不改变源对象的属性信息,比如DTO数据传输对象和数据对象DO,我们需要将DO对象进 ...

  6. 两难!到底用 Spring BeanUtils 还是 Apache BeanUtils?

    前言 在我们实际项目开发过程中,我们经常需要将不同的两个对象实例进行属性复制,从而基于源对象的属性信息进行后续操作,而不改变源对象的属性信息,比如DTO数据传输对象和数据对象DO,我们需要将DO对象进 ...

  7. BeanUtils.copyProperties() 用法

    转载自 https://blog.csdn.net/jdjdndhj/article/details/62056137 第一步: BeanUtils.copyProperties()与Property ...

  8. BeanUtils.copyProperties的用法

    前言 org.springframework.beans.BeanUtils,它提供了对java反射和自省API的包装.它里面还有很多工具类,这里我们介绍一下copyProperties. 我们如果有 ...

  9. 四条使用Spring BeanUtils的总结

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 绝色天龙 来源 | jianshu.com/p ...

最新文章

  1. python3x 中如何使用tkMessageBox
  2. 对Java基本数据类型的再思考
  3. redis 的惊群处理和分布式锁的应用例子
  4. Mysql之外连接_OUTER JOIN
  5. 就是要让你搞懂 Nginx,这篇就够了!
  6. Linux进程调度器-基础
  7. static详解java_java中static作用详解
  8. JVM虚拟机-Class文件简介
  9. 什么样的两个矩阵相似_Lecture 27 | 相似矩阵
  10. Axure高保真学校后台管理作品管理教师管理资源审核学生管理家长管理权限管理资源管理web端后台模板管理教师审核统计分析教育后台管理系统学校后台管理系统校园后台管理系统
  11. AppCan 携手腾讯微博开放平台共推跨平台开发工具
  12. 选择IT行业的自我心得,希望能帮助到各位!(一)
  13. 计算机运行时内存会超吗,我们不曾深纠的电脑技术 篇一:我们为什么要对内存进行超频?...
  14. Xvid 进行视频编码
  15. 模块化机房建设指导书_模块化数据中心机房建设方案
  16. 氚云tERP产品介绍-功能
  17. DHCP自动分配IP地址
  18. 统一监控报警平台架构设计思路
  19. 广度优先算法之狄克斯特拉算法
  20. 教你如何对产品做AB测试(abtest)

热门文章

  1. JVM-Java内存区域
  2. 在 Markdown 中,如何在反引号对语句中使用反引号
  3. 【小题目】输入两个数字以及一个符号,输出这两个数字在这个符号下运算产生的结果
  4. 0001-Hello world(第一弹)
  5. VMVMware-workstation以及CentOS-7安装
  6. 粘性控件,滑动停留StickLayout(导航栏滑动停留)
  7. jmeter之ip欺骗
  8. 一段人人都应该知道的从Vue到React的过渡史
  9. iometer-2006_07_27.common-src编译
  10. linux查看和修改当前系统时间