背景信息
最近在工作中,由于对象有太多的属性需要get,set ,对所有属性都这样操作的话,除了多余敲很多没有价值的代码量,还有点耽误时间。这个时候就考虑到可以使用 Spring BeanUtils 工具来直接实现把源对象 拷贝 到目标对象的操作,从而大大的节省人工通过get,set 方法来实现属性值获取与赋值的操作时间。
Spring BeanUtils
Spring BeanUtils 是 Spring Framework 核心模块中的一个实用工具类,主要用于 Java Bean 的属性操作和对象拷贝。它相比 Apache Commons BeanUtils 更加轻量且性能更好。
pom文件配置
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.3.0</version> </dependency>
主要优势
- 无额外依赖:Spring 项目天然集成,无需额外引入依赖
- 性能优化:相比 Apache Commons BeanUtils 性能更好
- 类型安全:更强的类型检查机制
- 简单易用:API 设计简洁明了
常用方法
- copyProperties(Object source, Object target)
将源对象中的属性值拷贝到目标对象中,只要属性名相同。 - copyProperties(Object source, Object target, Class<?> editable)
限制只拷贝editable类中定义的属性(通常用于限制只拷贝特定类中的属性,避免拷贝父类属性)。 - copyProperties(Object source, Object target, String... ignoreProperties)
拷贝属性,但忽略指定的属性名。 - copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties)
拷贝对象内容,限制只拷贝editable类中定义的属性,忽略指定的属性名
与 Apache Commons BeanUtils 的区别
- Spring BeanUtils 不提供类型转换,而 Apache Commons BeanUtils 提供了默认的类型转换(但效率较低)。
- Spring BeanUtils 在拷贝属性时,如果遇到类型不匹配,会抛出异常,而 Apache Commons BeanUtils 会尝试进行类型转换,如果转换失败则抛出异常。
- 性能上,Spring BeanUtils 通常比 Apache Commons BeanUtils 快,因为它没有使用反射缓存,而且没有类型转换的开销(但要求属性类型必须匹配)。
举例说明
这里我们准备三个对象:Student , Score , Info 对象,目标就是将 Student 和 Score 对象中的属性值拷贝到 Info 对象中
package org.example; public class Student { private String name; private int age; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } }
package org.example; public class Score { private int score; private String name; public int getScore() { return score; } public void setScore(int score) { this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
package org.example; public class Info { private String name; private int age; private String sex; private int score; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } }
下面我们开始测试,这里我们通过以下代码给 Student 属性赋值,给 Score 属性赋值,赋值后拷贝给 目标对象 Info 打印Info 对象的属性值信息
public static void main(String[] args) throws Exception { Student student = new Student(); student.setAge(10); student.setName("aa"); student.setSex("man"); Score score = new Score(); score.setScore(100); Info dest = new Info(); BeanUtils.copyProperties( score,dest); BeanUtils.copyProperties(student,dest); System.out.println(JSON.toJSONString(dest)); }
执行结果如图
这里我们可以看到我们的字段属性都已经拷贝成功了。但是这里我有一个地方需要说明一下,对于上面的 Score 对象,我们看到也有属性值 name 字段,同时在 main 方法中没有对 Score 对象的 name 字段赋值,那么此时如果拷贝的顺序有变化,比如对上面的 main 方法进行下面的调整
先将 Student 对象拷贝到 Info 对象,再将 Score 对象拷贝到 Info 对象,那么此时由于 Score 对象中也有 name 字段属性,且在 main 方法中没有赋值,且 后执行的拷贝,那么此时 Score 对象中的空 name 就会覆盖 前面对象 Student 对象的 name 字段属性值,结果就会导致 name 字段属性值丢失,就像这样的效果
因此在使用时,如果对多个源对象拷贝到目标对象操作,需要注意拷贝顺序,防止由此带来的字段属性值丢失,且不好排查。