找回密码
 立即注册
首页 业界区 安全 [Java/Spring] Spring BeanUtils:Java数据对象转换工具 ...

[Java/Spring] Spring BeanUtils:Java数据对象转换工具、对象属性操纵助手

准挝 2025-6-1 18:22:21
概述:Spring BeanUtils:Java数据对象转换工具、对象属性操纵助手

工具简介


  • Spring 的BeanUtils是Spring框架提供的一个实用工具类
它封装了一系列操作Java Bean的方法,旨在简化Java Bean属性的获取、设置、复制等操作。
通过使用BeanUtils,开发者可以更加高效地进行Java Bean之间的数据转换和属性操作,减少手动编写重复代码的工作量,提高代码的可读性和可维护性。


  • class: org.springframework.beans.BeanUtils
主要用途

Spring BeanUtils的主要用途:


  • 属性复制:将一个Java Bean的属性值复制到另一个Java Bean中
常用于DTO(Data Transfer Object)与数据库实体类(PO/Entity)之间的转换。


  • 属性值获取与设置:通过反射机制,动态地获取或设置Java Bean的属性值。
  • 属性描述:获取Java Bean属性的元数据,如属性名、类型等。
Spring的BeanUtils与Apache的BeanUtils区别

项Spring的BeanUtilsApache的BeanUtils性能好。
原因:对两个对象中相同名字的属性进行简单的get/set,仅检查属性的可访问性差。
原因:有很多检验:类型的转换、对象所属类的可访问性等
注意:此处类型转换是类似的类,多个String转为List是不行的。使用方面第1个参数是源,第2个参数是目标。
无需捕获异常第1个参数是目标,第2个参数是源。
必须捕获异常深/浅拷贝浅拷贝浅拷贝

  • Apache BeanUtils
  1. //source对应的对象成员赋值给target对应的对象成员
  2. BeanUtils.copyProperties(Object target, Object source);
  3. //只拷贝某些字段
  4. BeanUtils.copyProperties(Object target, String name, Object source)
复制代码

  • Apache的BeanUtils支持的类型转换
  1. java.lang.BigDecimal
  2. java.lang.BigInteger
  3. boolean and java.lang.Boolean
  4. byte and java.lang.Byte
  5. char and java.lang.Character
  6. java.lang.Class
  7. double and java.lang.Double
  8. float and java.lang.Float
  9. int and java.lang.Integer
  10. long and java.lang.Long
  11. short and java.lang.Short
  12. java.lang.String
  13. java.sql.Date
  14.     java.util.Date是不被支持的,而它的子类java.sql.Date是被支持的。
  15.     因此,如果对象包含时间类型的属性,且希望被转换的时候,一定要使用java.sql.Date类型。
  16.     否则,在转换时会提示`argument mistype`异常。
  17. java.sql.Time
  18. java.sql.Timestamp
复制代码
Maven坐标: spring-beans
  1. <dependency>
  2.   <groupId>org.springframework</groupId>
  3.   spring-beans</artifactId>
  4.   
  5.   <version>${spring.version}</version>
  6. </dependency>
复制代码
核心API

copyProperties(Object source, Object target) : 从源Bean对象复制到目标Bean对象


  • 用途:将一个Java Bean的属性值复制到另一个Java Bean中。
  • 参数说明:


  • source:源对象,即 属性值来源的Java Bean。
  • target:目标对象,即 属性值将被复制到的Java Bean。


  • 示例代码
  1. User user = new User();  
  2. user.setName("John");  
  3. user.setAge(19);  
  4. UserDTO userDTO = new UserDTO();  
  5. BeanUtils.copyProperties(user, userDTO);  
  6. System.out.println(userDTO.getName()); // 输出:John
复制代码
instantiateClass(Class clazz) : 实例化一个Bean对象


  • 用途:通过反射机制实例化一个Java类。
  • 参数说明:


  • clazz:需要实例化的类的Class对象。


  • 示例代码
  1. User user = (User) BeanUtils.instantiateClass(User.class);
复制代码
populate(Object obj, PropertyValues pvs) : 为目标Bean对象设置属性值


  • 用途:根据PropertyValues对象设置Java Bean的属性值。
  • 参数说明:


  • obj:需要设置属性的Java Bean对象。
  • pvs:包含属性值的PropertyValues对象。


  • 示例代码
  1. MutablePropertyValues pvs = new MutablePropertyValues();  
  2. pvs.add("name", "John");  
  3. pvs.add("age", 30);  
  4.   
  5. User user = new User();  
  6. BeanUtils.populate(user, pvs);  
  7. System.out.println(user.getName()); // 输出:John
复制代码
getPropertyDescriptors(Class clazz) :


  • 用途:返回指定类的所有JavaBean属性描述符数组。
  • 参数说明:


  • clazz:需要获取属性描述符的类的Class对象。


  • 示例代码
  1. PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(User.class);  
  2. for (PropertyDescriptor descriptor : descriptors) {  
  3.     System.out.println(descriptor.getName());  
  4. }
复制代码
describe(Object obj)


  • 用途:返回指定对象的JavaBean属性描述符的Map。
  • 参数说明:


  • obj:需要描述属性的对象。


  • 示例代码
  1. User user = new User();  
  2. user.setName("John");  
  3. Map<String, PropertyDescriptor> descriptors = BeanUtils.describe(user);  
  4. for (Map.Entry<String, PropertyDescriptor> entry : descriptors.entrySet()) {  
  5.     System.out.println(entry.getKey());  
  6. }
复制代码
findPropertyDescriptors(Class clazz, PropertyDescriptor… excluded)


  • 用途:返回指定类的JavaBean属性描述符数组,可以排除指定的属性描述符。
  • 参数说明:


  • clazz:需要获取属性描述符的类的Class对象。
  • excluded:需要排除的属性描述符数组。


  • 示例代码
  1. // 排除id属性
  2. PropertyDescriptor[] descriptors = BeanUtils.findPropertyDescriptors(User.class, "id");
复制代码
isSimpleProperty(Class type)


  • 用途:判断给定的类是否是一个简单的JavaBean属性类型(如基本类型、String等)。
  • 参数说明:


  • type:需要判断的类的Class对象。


  • 示例代码
  1. boolean isSimple = BeanUtils.isSimpleProperty(String.class); // 返回true  
  2. boolean isComplex = BeanUtils.isSimpleProperty(User.class); // 返回false
复制代码
getSimplePropertyType(PropertyDescriptor descriptor)


  • 用途:获取指定属性描述符所描述的属性的简单类型。
  • 参数说明:


  • descriptor:属性描述符。


  • 示例代码
  1. PropertyDescriptor descriptor = new PropertyDescriptor("name", User.class);  
  2. Class<?> simpleType = BeanUtils.getSimplePropertyType(descriptor);  
  3. System.out.println(simpleType.getName()); // 输出:java.lang.String
复制代码
getPropertyValue(Object bean, String name)


  • 用途:获取指定Java Bean对象的属性值。
  • 参数说明:


  • bean:Java Bean对象。
  • name:属性名。


  • 示例代码
  1. User user = new User();  
  2. user.setName("John");  
  3. Object value = BeanUtils.getPropertyValue(user, "name");  
  4. System.out.println(value); // 输出:John
复制代码
setPropertyValue(Object bean, String name, Object value)


  • 用途:设置指定Java Bean对象的属性值。
  • 参数说明:


  • bean:Java Bean对象。
  • name:属性名。
  • value:属性值。


  • 示例代码
  1. User user = new User();  
  2. BeanUtils.setPropertyValue(user, "name", "John");  
  3. System.out.println(user.getName()); // 输出:John
复制代码
原型设计模式:浅拷贝 vs 深拷贝


  • Spring的BeanUtils是浅拷贝


  • 泛型只在编译期起作用,不能依靠泛型来做运行期的限制;


  • 原型模式:浅拷贝和深拷贝


  • 浅拷贝:对基本数据类型进行值传递,对引用数据类型,使用其引用地址,不拷贝其内容,此为浅拷贝
  • 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
实验: Spring BeanUtils 是浅拷贝


  • 对象拷贝
就是将一个类中的属性拷贝到另一个中,对于 Spring BeanUtils.copyProperties 来说,必须保证属性名和类型是相同的,因为它是根据get和set方法来赋值的


  • 浅拷贝:对于基本数据类型就是直接进行值传递,在内存的另一个空间内存放,修改这个值不会影响到拷贝源的值
浅拷贝对于引用数据类型就是进行的是地址传递,并没有对该对象重新开辟一个内存空间进行存放,所以对于引用数据类型的浅拷贝就相当于两个引用指向了同一个内存地址
浅拷贝,可以理解为如果是引用类型,那么目标对象拷贝的只是源对象的地址,无论目标对象还是源对象改变,他们都会一起改变


  • 深拷贝
就是将目标对象的属性全部复制一份给源对象,复制完之后他们就是隔开的,没有任何关系,无论操作源对象还是目标对象都对另一个没有影响
无论是浅拷贝还是深拷贝,对于基本类型和String来说都是没有影响的,有影响的只有引用类型数据
1.png
  1. import org.springframework.beans.BeanUtils;
  2. import java.util.Arrays;
  3. import java.util.List;
  4. import java.util.stream.Collectors;
  5. public class Main {
  6.     public static void main(String[] args) {
  7.         // 要是用stream流,一般会使用到集合,先构造一个master集合
  8.         List<Master> main = Arrays.asList(new Master("男"), new Master("女"));
  9.         main = main.stream().map(m -> {
  10.             m.setPet(new Pet("源pet", 5));
  11.             return m;
  12.         }).collect(Collectors.toList());
  13.         // 使用stream流实现bean的深拷贝
  14.         List<Master> vice = main
  15.                 .stream()
  16.                 .map(m -> {
  17.                     Master temp = new Master();
  18.                     BeanUtils.copyProperties(m, temp);
  19.                     return temp;
  20.                 }).collect(Collectors.toList());
  21.         System.out.println("主对象: " + main);
  22.         System.out.println("副对象: " + vice);
  23.         System.out.println("拷贝后对象的指针是否都指向同一个堆内存: " + (main == vice));
  24.         System.out.println();
  25.         // 修改副对象中第一个元素的性别属性
  26.         vice.get(0).setGender("女");
  27.         System.out.println("主对象: " + main);
  28.         System.out.println("副对象: " + vice);
  29.         System.out.println("在拷贝对象中,修改第一个元素的String类型的属性gender后是否相同: " + main.get(0).getGender().equals(vice.get(0).getGender()));
  30.         System.out.println();
  31.         // 修改副对象中第一个元素的引用对象中的年龄、名称属性
  32.         vice.get(0).getPet().setAge(10);
  33.         vice.get(0).getPet().setName("小猫");
  34.         System.out.println("主对象: " + main);
  35.         System.out.println("副对象: " + vice);
  36.         System.out.println("在拷贝对象中,修改第一个元素的引用类型Pet后,是否在同一个堆内存当中: " + (main.get(0).getPet() == vice.get(0).getPet()));
  37.         System.out.println();
  38.         System.out.println("可以发现,它是对'主'对象的拷贝,它不会拷贝'主'对象深层次的可变对象,只做第一层的拷贝");
  39.     }
  40. }
  41. // 宠物对象
  42. class Pet {
  43.     private String name;
  44.     private int age;
  45.     public Pet(String name, int age) {
  46.         this.name = name;
  47.         this.age = age;
  48.     }
  49.         // ...省略set、get方法
  50.     @Override
  51.     public String toString() {
  52.         return "Pet{" +
  53.                 "name='" + name + '\'' +
  54.                 ", age=" + age +
  55.                 '}';
  56.     }
  57. }
  58. // 主人对象
  59. class Master {
  60.     private String gender;
  61.     private Pet pet;
  62.     public Master(String gender) {
  63.         this.gender = gender;
  64.     }
  65.     public Master() {
  66.     }
  67.         // ...省略set、get方法
  68.     @Override
  69.     public String toString() {
  70.         return "Master{" +
  71.                 "gender='" + gender + '\'' +
  72.                 ", pet=" + pet +
  73.                 '}';
  74.     }
  75. }
复制代码
2.png

案例实践

案例1: User 拷贝/转换为 UserDTO
  1. import lombok.Getter;
  2. import lombok.NoArgsConstructor;
  3. import lombok.Setter;
  4. import org.springframework.beans.BeanUtils;
  5. /**
  6. * @reference-doc
  7. *  [1] spring中你们是在那里知道的BeanUtils? - Zhihu - https://www.zhihu.com/question/490241175/answer/3305507642
  8. */
  9. public class BeanUtilsExample {
  10.     public static void main(String[] args) {
  11.         // 创建一个User对象并设置属性
  12.         User user = new User();
  13.         user.setName("张三");
  14.         user.setEmail("zhangsan@example.com");
  15.         user.setAge(30);
  16.         // 创建一个空的UserDTO对象
  17.         UserDTO userDTO = new UserDTO();
  18.         // 使用BeanUtils复制属性
  19.         BeanUtils.copyProperties(user, userDTO);
  20.         // 打印UserDTO的属性,验证是否复制成功
  21.         System.out.println("UserDTO Name: " + userDTO.getName());
  22.         System.out.println("UserDTO Email: " + userDTO.getEmail());
  23.         System.out.println("UserDTO Age: " + userDTO.getAge());
  24.     }
  25. }
  26. @Getter
  27. @Setter
  28. @NoArgsConstructor
  29. class User {
  30.     private String name;
  31.     private String email;
  32.     private int age;
  33. }
  34. @Getter
  35. @Setter
  36. @NoArgsConstructor
  37. class UserDTO {
  38.     private String name;
  39.     private String email;
  40.     private int age;
  41. }
复制代码
log
  1. UserDTO Name: 张三
  2. UserDTO Email: zhangsan@example.com
  3. UserDTO Age: 30
复制代码
Y 推荐文献


  • [Java EE] 企业级Java Web应用的概念辨析: POJO(PO / DTO / VO) | BO/DO | DAO - 博客园/千千寰宇
  • MapStruct:Java数据对象的映射框架 - 博客园/千千寰宇
X 参考文献


  • Spring BeanUtils:灵活高效的JavaBean操作助手 - CSDN
  • spring中你们是在那里知道的BeanUtils? - Zhihu
  • Spring学习笔记(一)【BeanUtils.copyProperties方法】 - CSDN
浅拷贝、深拷贝 【推荐】


  • Spring工具类--BeanUtils--使用/实例 - CSDN
    本文作者:        千千寰宇   
    本文链接:         https://www.cnblogs.com/johnnyzen   
    关于博文:评论和私信会在第一时间回复,或直接私信我。   
    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA     许可协议。转载请注明出处!
    日常交流:大数据与软件开发-QQ交流群: 774386015        【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!   

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册