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

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

阴昭昭 2025-6-1 18:21:26



  • 此文是Java数据对象转换工具系列的最后1篇。
  • 本文(Apache Commons BeanUtils)主要参考自:
Apache Commons BeanUtils: JavaBean操作的艺术 - Segmentfaul
概述:Apache Commons BeanUtils:Java数据对象转换工具、对象属性操纵助手

1.png

Apache Commons BeanUtils, 这货简直就是处理JavaBean的利器,用起来又方便又快捷。
不管是属性拷贝类型转换,还是动态访问,BeanUtils都能轻松应对。
BeanUtils是啥?


  • Apache Commons BeanUtils,简单来说,就是一套Java库,专门用来操作Java Beans。
  • 什么是 Java Beans?
嗯,它其实就是遵循特定规范的Java数据模型类,比如有无参构造器、属性私有、公共的getter和setter方法。
这些 Beans 在 Java 世界里可是处处可见,无论是Web开发还是桌面应用,它们都扮演着重要角色。
为啥要用BeanUtils?


  • 操作Java Bean虽然不难,但手动去写一堆getter和setter很麻烦,是不是觉得有点啰嗦?
特别是要处理一堆类似的操作,比如复制属性啊,类型转换啊,要写大量的价值不高的代码


  • 这时候,BeanUtils就闪亮登场了。
它能让这些操作变得简单快捷,代码更加整洁,提高开发效率。
怎么用起来?

Step1 引入依赖(Maven)

先来看看如何把BeanUtils加入到咱们的项目里。
一般来说,用Maven或者Gradle这样的构建工具是最方便的。
例如,用Maven的话,只需在pom.xml文件里添加如下依赖:
  1. <dependency>
  2.     <groupId>commons-beanutils</groupId>
  3.     commons-beanutils</artifactId>
  4.     <version>${commons-beansutils.version}</version>
  5. </dependency>
复制代码
加完这个依赖后,咱们就可以在项目中自由使用BeanUtils的各种功能了。
Step2 API的调用

参见下一章节。
核心功能概览

PropertyUtils:操控属性的基础

PropertyUtils主要用来操作Bean的属性。比如说,咱们可以通过它来获取或设置属性的值。它更像是BeanUtils的基石,为BeanUtils提供了基本的属性操作功能。
BeanUtils:PropertyUtils的超级版


  • 如果说PropertyUtils是基础版,那BeanUtils就是加强版
它在PropertyUtils的基础上增加了很多实用的功能,比如属性的复制。这可是在实际开发中超级常用的。


  • BeanUtils的常用方法?
方法说明cloneBean(Object bean):Object对象的克隆copyProperties(Object dest, Object orig):void对象的拷贝copyProperty(Object bean, String name, Object value)拷贝指定的属性值到指定的bean
将对象orig的属性值赋值给对象dest对象对应的属性
注意:只有属性名名相同类型一致的才会赋值成功。setProperty(Object bean, String name, Object value):void设置指定属性的值
给指定对象bean的指定name属性赋值为指定值value。
如果指定的属性不存在,则什么也不发生。getProperty(Object bean, String name):String获取指定对象bean指定name属性的值。
如果指定的属性不存在,则会抛出异常。
注意:当属性的类型是数组类型时,获取到的值数组中的第一个值。populate(Object bean, Map properties):void将map数据拷贝到javabean中(map的key要与javabean的属性名称一致
将一个Map集合中的数据封装到指定对象bean中
注意:对象bean的属性名和Map集合中key要相同。

  • BeanUtils与PropertyUtils的区别?
BeanUtils与PropertyUtils这两个类几乎有一样的功能,唯一的区别是:BeanUtils在对Bean赋值是会进行类型转化
举例来说,也就是在copyProperty时只要属性名相同,就算类型不同,BeanUtils也可以进行copy;
而PropertyBeanUtils则可能会报错!!
当然2个Bean之间的同名属性的类型必须是可以转化的,否则用BeanUtils一样会报错。
若实现了org.apache.commons.beanutils.Converter接口则可以自定义类型之间的转化
由于不做类型转化,用PropertyUtils在性能上会有很大提高!
ConvertUtils:类型转换的神器

在处理JavaBean的时候,经常会遇到属性类型转换的需求。
这时候,ConvertUtils就能派上用场了。它能自动帮咱们处理各种类型之间的转换,省心省力。
代码示例:基本使用

来,咱们看个简单的例子。假设有个人物类(Person),用BeanUtils给它设置属性。
  1. import org.apache.commons.beanutils.BeanUtils;
  2. public class Demo {
  3.     public static void main(String[] args) {
  4.         Person person = new Person();
  5.         try {
  6.             // 使用BeanUtils设置属性
  7.             BeanUtils.setProperty(person, "name", "张三");
  8.             BeanUtils.setProperty(person, "age", 30);
  9.             // 获取并打印属性
  10.             String name = BeanUtils.getProperty(person, "name");
  11.             String age = BeanUtils.getProperty(person, "age");
  12.             System.out.println("姓名: " + name + ", 年龄: " + age);
  13.         } catch (Exception e) {
  14.             e.printStackTrace();
  15.         }
  16.     }
  17. }
  18. class Person {
  19.     private String name;
  20.     private int age;
  21.     // 省略getter和setter方法
  22. }
复制代码
2.png

在这个例子中,咱们用BeanUtils给Person类的name和age属性赋值,然后又获取这些值并打印出来。
深入PropertyUtils

PropertyUtils的核心功能


  • PropertyUtils主要提供了读取和设置JavaBean属性的功能。
这听起来很基础,但在实际开发中却非常有用。
举个例子,如果咱们需要从一个对象中读取某个属性的值,或者要把值设置到对象的某个属性上,用PropertyUtils就能轻松搞定。
读取属性:获取对象属性的艺术

咱们来看看如何使用PropertyUtils读取属性值。
假设有个User类,有name和age这两个属性,现在要读取这些属性的值。
  1. import org.apache.commons.beanutils.PropertyUtils;
  2. public class PropertyUtilsDemo {
  3.     public static void main(String[] args) {
  4.         User user = new User();
  5.         user.setName("李雷");
  6.         user.setAge(25);
  7.         try {
  8.             // 读取属性值
  9.             String name = (String) PropertyUtils.getProperty(user, "name");
  10.             Integer age = (Integer) PropertyUtils.getProperty(user, "age");
  11.             System.out.println("姓名: " + name + ", 年龄: " + age);
  12.         } catch (Exception e) {
  13.             e.printStackTrace();
  14.         }
  15.     }
  16. }
  17. class User {
  18.     private String name;
  19.     private int age;
  20.     // 省略getter和setter方法
  21. }
复制代码
在这个例子中,通过PropertyUtils.getProperty方法轻松获取了User对象的name和age属性值。
设置属性:给对象属性赋值的智慧


  • 接下来,如果咱们想要设置对象的属性值,PropertyUtils同样能派上用场。
比如现在要把User的name改成“韩梅梅”,age改成30。
  1. public class PropertyUtilsDemo {
  2.     public static void main(String[] args) {
  3.         User user = new User();
  4.         try {
  5.             // 设置属性值
  6.             PropertyUtils.setProperty(user, "name", "韩梅梅");
  7.             PropertyUtils.setProperty(user, "age", 30);
  8.             // 验证结果
  9.             System.out.println("姓名: " + user.getName() + ", 年龄: " + user.getAge());
  10.         } catch (Exception e) {
  11.             e.printStackTrace();
  12.         }
  13.     }
  14.     // User类和前面一样,这里就不重复了
  15. }
复制代码
在这个例子里,用PropertyUtils.setProperty方法给User对象的属性赋了新值,然后打印出来,确保一切正常。
动态属性操作:灵活性的体现


  • PropertyUtils的魔力还不止于此。
它还支持动态属性操作,这意味着咱们可以在运行时动态地读取和设置属性,而不必在编码时就确定属性名。这在处理动态数据结构时特别有用。
  1. import java.util.HashMap;
  2. import java.util.Map;
  3. public class DynamicPropertyDemo {
  4.     public static void main(String[] args) {
  5.         Map<String, Object> map = new HashMap<>();
  6.         map.put("username", "小明");
  7.         map.put("age", 20);
  8.         try {
  9.             // 动态读取属性
  10.             String username = (String) PropertyUtils.getProperty(map, "username");
  11.             Integer age = (Integer) PropertyUtils.getProperty(map, "age");
  12.             System.out.println("用户名: " + username + ", 年龄: " + age);
  13.             // 动态设置属性
  14.             PropertyUtils.setProperty(map, "age", 21);
  15.             System.out.println("更新后年龄: " + map.get("age"));
  16.         } catch (Exception e) {
  17.             e.printStackTrace();
  18.         }
  19.     }
  20. }
复制代码
在这个例子中,创建了一个Map,然后用PropertyUtils来动态地处理这个Map中的数据。
这样的灵活性在处理JSON数据或者动态表单数据时特别有优势。
BeanUtils的高级应用

现在要和大家探讨的是BeanUtils的一些高级应用,特别是属性复制和动态Bean操作。
这些功能在实际开发中非常有用,可以让代码更加简洁和高效。
属性复制/copyProperties方法:简化数据迁移


  • 属性复制是BeanUtils的一大亮点。
在实际开发中,经常会遇到从一个对象复制属性到另一个对象的场景,尤其是在处理类似DTO(数据传输对象)和Entity(实体)转换的时候。
如果手动一个属性一个属性地复制,既麻烦又容易出错。
这时候,BeanUtils的copyProperties方法就能大显身手了。
看看怎么用这个功能:
  1. import org.apache.commons.beanutils.BeanUtils;
  2. public class CopyPropertiesDemo {
  3.     public static void main(String[] args) {
  4.         UserDTO userDTO = new UserDTO("王小明", 28);
  5.         UserEntity userEntity = new UserEntity();
  6.         try {
  7.             // 从DTO复制到实体
  8.             BeanUtils.copyProperties(userEntity, userDTO);
  9.             // 验证结果
  10.             System.out.println("用户实体:姓名 - " + userEntity.getName() + ", 年龄 - " + userEntity.getAge());
  11.         } catch (Exception e) {
  12.             e.printStackTrace();
  13.         }
  14.     }
  15. }
  16. class UserDTO {
  17.     private String name;
  18.     private int age;
  19.     UserDTO(String name, int age) {
  20.         this.name = name;
  21.         this.age = age;
  22.     }
  23.     // 省略getter和setter方法
  24. }
  25. class UserEntity {
  26.     private String name;
  27.     private int age;
  28.     // 省略getter和setter方法
  29. }
复制代码
在这个例子中,从一个UserDTO对象复制属性到UserEntity对象。这样一来,所有的属性就自动从DTO转移到实体上了,省时省力。
populate方法 : 为对象属性赋值
  1. public class App {
  2.     public static void main( String[] args ) {
  3.         Dog dog = new Dog();
  4.         Map<String, Object> map = new HashMap<>();
  5.         map.put("age",2);
  6.         map.put("name","huahua");
  7.         try {
  8.             BeanUtils.populate(dog,map);
  9.         } catch (IllegalAccessException e) {
  10.             e.printStackTrace();
  11.         } catch (InvocationTargetException e) {
  12.             e.printStackTrace();
  13.         } finally {
  14.         }
  15.         System.out.println("dog:"+dog);//dog:Dog{name='huahua', age=2}
  16.     }
  17. }
  18. //Dog 类:
  19. public class Dog {
  20.     private String name;
  21.     private int age;
  22.     public void setName(String name) {
  23.         this.name = name;
  24.     }
  25.     public void setAge(int age) {
  26.         this.age = age;
  27.     }
  28.     public String getName() {
  29.         return name;
  30.     }
  31.     public int getAge() {
  32.         return age;
  33.     }
  34.     @Override
  35.     public String toString() {
  36.         return "Dog{" +
  37.                 "name='" + name + '\'' +
  38.                 ", age=" + age +
  39.                 '}';
  40.     }
  41. }
复制代码
cloneBean方法:拷贝对象
  1. // Animal 类
  2. public class Animal {
  3.     private String name;
  4.     private String type;
  5.     private int age;
  6.     public String getName() {
  7.         return name;
  8.     }
  9.     public void setName(String name) {
  10.         this.name = name;
  11.     }
  12.     public String getType() {
  13.         return type;
  14.     }
  15.     public void setType(String type) {
  16.         this.type = type;
  17.     }
  18.     public int getAge() {
  19.         return age;
  20.     }
  21.     public void setAge(int age) {
  22.         this.age = age;
  23.     }
  24.     @Override
  25.     public String toString() {
  26.         return "Animal{" +
  27.                 "name='" + name + '\'' +
  28.                 ", type='" + type + '\'' +
  29.                 ", age=" + age +
  30.                 '}';
  31.     }
  32. }
  33. //测试类
  34. public class App
  35. {
  36.     public static void main( String[] args ) throws IllegalAccessException,InvocationTargetException
  37.     {
  38.         // copyProperties 方法
  39.         Animal animal = new Animal();
  40.         animal.setName("mimi");
  41.         animal.setType("dog");
  42.         animal.setAge(2);
  43.         Dog dog = new Dog();
  44.         BeanUtils.copyProperties(dog,animal);
  45.         System.out.println("将 animal 值赋给 dog:");
  46.         System.out.println(dog);
  47.         //如果被copy对象缺少目标对象中的某些属性,那么目标对象中的对应值为 null
  48.         Animal animal1 = new Animal();
  49.         BeanUtils.copyProperties(animal1,dog);
  50.         System.out.println("将 dog 值赋给 animal1 对象:");
  51.         System.out.println(animal1);
  52.         // copyProperty 方法
  53.         animal.setName("huahua");
  54.         BeanUtils.copyProperty(dog,"name",animal.getName());
  55.         System.out.println("改变 animal name属性,并赋给 dog:");
  56.         System.out.println(dog);
  57.         // setProperty 方法
  58.         BeanUtils.setProperty(dog,"age",3);
  59.         System.out.println("给 dog 的 age 属性赋值:");
  60.         System.out.println(dog);
  61.         // cloneBean 方法
  62.         try {
  63.             Animal animal2 = (Animal)BeanUtils.cloneBean(animal1);
  64.             System.out.println("通过 animal1 克隆 animal2 对象:");
  65.             System.out.println(animal2);
  66.         } catch (InstantiationException e) {
  67.             e.printStackTrace();
  68.         } catch (NoSuchMethodException e) {
  69.             e.printStackTrace();
  70.         }
  71.     }
  72. }
复制代码
out
  1. 将 animal 值赋给 dog:
  2. Dog{name='mimi', age=2}
  3. 将 dog 值赋给 animal1 对象:
  4. Animal{name='mimi', type='null', age=2}
  5. 改变 animal name属性,并赋给 dog:
  6. Dog{name='huahua', age=2}
  7. 给 dog 的 age 属性赋值:
  8. Dog{name='huahua', age=3}
  9. 通过 animal1 克隆 animal2 对象:
  10. Animal{name='mimi', type='null', age=2}
复制代码
动态Bean操作:更多的可能性(DynaBean/DynaClass/LazyDynaBean/LazyDynaClass)


  • 动态Bean操作是BeanUtils中另一个很酷的功能。它允许咱们在运行时动态创建和操作Bean,这在处理不确定的数据结构或者动态生成对象的场景下特别有用。
  1. import org.apache.commons.beanutils.DynaBean;
  2. import org.apache.commons.beanutils.DynaClass;
  3. import org.apache.commons.beanutils.LazyDynaBean;
  4. import org.apache.commons.beanutils.LazyDynaClass;
  5. public class DynamicBeanDemo {
  6.     public static void main(String[] args) {
  7.         // 创建一个动态Bean
  8.         DynaClass dynaClass = new LazyDynaClass();
  9.         DynaBean dynaBean = new LazyDynaBean(dynaClass);
  10.         try {
  11.             // 动态添加属性
  12.             dynaBean.set("name", "李华");
  13.             dynaBean.set("age", 30);
  14.             // 读取属性值
  15.             String name = (String) dynaBean.get("name");
  16.             Integer age = (Integer) dynaBean.get("age");
  17.             System.out.println("动态Bean:姓名 - " + name + ", 年龄 - " + age);
  18.         } catch (Exception e) {
  19.             e.printStackTrace();
  20.         }
  21.     }
  22. }
复制代码
在这个例子里,创建了一个动态Bean,然后给它添加了name和age两个属性。这种方式的灵活性非常高,可以根据需要动态定义和操作对象的属性。
ConvertUtils的威力


  • ConvertUtils,这个小玩意儿主要负责类型转换,特别是在处理JavaBean的属性时,经常会遇到需要把一种类型转换成另一种类型的情况。ConvertUtils就是用来解决这类问题的。
类型转换:简化而高效


  • 在Java开发中,类型转换无处不在。
比如从字符串转换成整数,或者从整数转换成布尔值等等。
这些操作听起来简单,但如果每次都手动写转换代码,不仅麻烦,而且容易出错。
ConvertUtils提供了一种统一的解决方案,可以自动完成这些转换,简化开发流程。
使用ConvertUtils进行基本转换

案例: convert 方法的使用

来看看ConvertUtils的基本使用方法。假设现在有个字符串表示的年龄,需要把它转换成整数类型。
  1. import org.apache.commons.beanutils.ConvertUtils;
  2. public class ConvertUtilsDemo {
  3.     public static void main(String[] args) {
  4.         // 字符串转换为整数
  5.         String ageStr = "25";
  6.         Integer age = (Integer) ConvertUtils.convert(ageStr, Integer.class);
  7.         System.out.println("转换后的年龄: " + age);
  8.     }
  9. }
复制代码
在这个例子中,使用了ConvertUtils的convert方法,轻松地把字符串"25"转换成了整数。
自定义类型转换器


  • 但ConvertUtils的真正魅力在于它的可扩展性
如果咱们需要处理一些特殊的转换,比如把字符串转换成日期类型,或者把数字转换成自定义的枚举类型,这时就可以自定义转换器
  1. import org.apache.commons.beanutils.Converter;
  2. import org.apache.commons.beanutils.ConvertUtils;
  3. import org.apache.commons.beanutils.converters.DateTimeConverter;
  4. public class CustomConverterDemo {
  5.     public static void main(String[] args) {
  6.         // 自定义转换器:字符串转日期
  7.         DateTimeConverter dtConverter = new DateTimeConverter() {
  8.             @Override
  9.             protected Class<?> getDefaultType() {
  10.                 return java.util.Date.class;
  11.             }
  12.         };
  13.         ConvertUtils.register(dtConverter, java.util.Date.class);
  14.         // 使用自定义转换器
  15.         String dateStr = "2023-12-25";
  16.         java.util.Date date = (java.util.Date) ConvertUtils.convert(dateStr, java.util.Date.class);
  17.         System.out.println("转换后的日期: " + date);
  18.     }
  19. }
复制代码
在这个例子里,注册了一个自定义的日期转换器,用来把字符串转换成java.util.Date类型。这样的自定义转换器让ConvertUtils的功能更加强大和灵活。
综合案例:处理复杂转换


  • 实际开发中,咱们可能会遇到更复杂的转换需求。
比如,有一个用户信息的字符串,里面包含了姓名、年龄和生日,咱们需要把这些信息提取出来,转换成相应的数据类型。
  1. public class ComplexConversionDemo {
  2.     public static void main(String[] args) {
  3.         // 假设有这样一个用户信息字符串
  4.         String userInfo = "张三,30,1993-04-15";
  5.         // 分割字符串
  6.         String[] parts = userInfo.split(",");
  7.         // 转换各个部分
  8.         String name = parts[0];
  9.         Integer age = (Integer) ConvertUtils.convert(parts[1], Integer.class);
  10.         java.util.Date birthday = (java.util.Date) ConvertUtils.convert(parts[2], java.util.Date.class);
  11.         // 输出结果
  12.         System.out.println("姓名: " + name + ", 年龄: " + age + ", 生日: " + birthday);
  13.     }
  14. }
复制代码
在这个例子中,处理了一个包含多种数据类型的字符串,并且使用ConvertUtils轻松完成了类型转换。
性能分析

性能


  • 虽然BeanUtils提供了很多便利的功能,但这些功能的背后可能会有一定的性能代价。
例如,在复制大量属性或频繁操作Bean时,性能问题可能会浮现。
这不是说BeanUtils性能差,而是任何便捷的功能都可能有性能成本,了解这一点对于写出高效的代码很重要。
BeanUtils的性能分析


  • BeanUtils在进行属性复制类型转换时,会使用反射机制
反射机制虽然提供了极大的灵活性,但与直接访问属性相比,它在性能上通常会慢一些
这是因为:反射需要在运行时解析类的元数据,这个过程比直接执行编译过的Java代码
最佳实践与总结

实际应用中的考量


  • 数据量和频率:在处理小量数据不频繁的操作时,BeanUtils带来的性能影响可以忽略不计。但在大批量数据处理高频调用场景下,性能差异可能变得显著。
  • 功能与性能的平衡:在选择使用BeanUtils时,需要权衡其提供的便利性和可能的性能代价。如果性能是关键因素,可能需要考虑替代方案或优化代码。
最佳实践


  • 合理使用反射:BeanUtils依赖于反射来操作JavaBean的属性。虽然反射很强大,但也不是万能的。在性能敏感的场景下,考虑直接使用getter和setter方法。
  • 避免过度依赖:虽然BeanUtils可以简化很多操作,但并不意味着所有属性操作都应该通过它来完成。评估每种情况,如果手动操作更简单明了,就没必要强行使用BeanUtils。
  • 处理异常:BeanUtils的方法可能会抛出异常。妥善处理这些异常,不仅可以避免程序崩溃,还能帮助定位问题。
  • 自定义转换器的使用:当遇到BeanUtils内置转换器无法满足需求时,可以自定义转换器。但要确保自定义转换器的正确性和效率。
  • 利用缓存提高性能:如果在高频率操作的场景中使用BeanUtils,考虑使用缓存机制来存储反射结果,以提高性能。
常见错误


  • 忽略了null值的处理:在复制属性时,BeanUtils会将源对象的null值也复制过去。在某些情况下,这可能会覆盖目标对象的现有值。要特别注意这一点。
  • 不正确的数据类型:在使用ConvertUtils进行类型转换时,如果源数据类型和目标数据类型不匹配,可能会导致转换错误或数据丢失。
  • 反射性能问题:忽视BeanUtils基于反射的特性可能会导致性能问题,特别是在大数据量或高频率操作的场景中。
  • 异常处理不当:BeanUtils操作可能会抛出多种异常。忽略这些异常的正确处理,可能会导致程序中断或隐蔽的bug。
总结


  • 在本文中,我们深入探讨了Apache Commons BeanUtils,一个在Java开发中不可或缺的工具。
从基本介绍到高级应用,本文全面覆盖了BeanUtils的核心功能,包括PropertyUtils和ConvertUtils的使用,突出了它们在处理JavaBean属性时的便利性和灵活性。


  • 我们首先概述了BeanUtils的基础知识,强调了它在简化JavaBean操作中的作用。随后,详细探讨了PropertyUtils和BeanUtils的高级功能,如属性复制和动态Bean操作,展示了它们如何在数据转换和处理中提供高效的解决方案。特别是在涉及自定义类型转换器和处理复杂类型转换的场景中,BeanUtils显示出其强大的功能。
最后希望大家能够学有所获,提升效率,简化开发!
Y 推荐文献


  • [Java EE] 企业级Java Web应用的概念辨析: POJO(PO / DTO / VO) | BO/DO | DAO  - 博客园/千千寰宇
  • Spring BeanUtils:Java数据对象转换工具、对象属性操纵助手 - 博客园/千千寰宇
含:Spring BeanUtils 与 Apache Commons BeanUtils 的比较


  • Apache Commons


  • https://commons.apache.org/proper/commons-beanutils/
X 参考文献


  • Apache Commons BeanUtils: JavaBean操作的艺术 - Segmentfault 【推荐】
2023.12.25


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

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