1. 概述

Apache Commons BeanUtils 提供了处理 Java Bean 的全套工具。简单来说,Java Bean 就是一个包含字段、getter/setter 方法和无参构造函数的普通 Java 类。

虽然 Java 原生提供了反射和内省机制来识别并动态调用 getter/setter,但这些 API 学习曲线陡峭,且常常需要开发者编写大量模板代码。BeanUtils 就是来解决这个痛点的,它用简单粗暴的方式封装了底层操作。

核心优势

简化反射操作:一行代码替代冗长的反射逻辑
类型安全:自动处理类型转换
减少样板代码:告别枯燥的属性访问写法


2. Maven 依赖

使用前先在 pom.xml 中添加依赖(版本选最新的 stable 版):

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
</dependency>

3. 创建 Java Bean

我们创建两个典型的 Bean 类:CourseStudent注意:属性命名要符合规范(比如 enrolledStudent 而不是 _enrolledStudent)。

public class Course {
    private String name;
    private List<String> codes;
    private Map<String, Student> enrolledStudent = new HashMap<>();

    // 标准的 getter/setter
}
public class Student {
    private String name;

    // 标准的 getter/setter
}

设计说明

  • Course 包含课程名称、课程代码列表
  • enrolledStudent 是 Map 结构:Key 是学号(如 "ST-1"),Value 是 Student 对象

4. 属性访问

Bean 属性分三类:简单属性、索引属性、映射属性。PropertyUtils 是核心操作类。

4.1 简单属性

指单值属性(基本类型或复杂对象)。操作示例

Course course = new Course();
String name = "Computer Science";
List<String> codes = Arrays.asList("CS", "CS01");

PropertyUtils.setSimpleProperty(course, "name", name);
PropertyUtils.setSimpleProperty(course, "codes", codes);

⚠️ 踩坑点:属性名必须精确匹配(区分大小写)

4.2 索引属性

指可通过索引访问的集合类型(如 List)。修改特定索引值

// 将 codes 列表的第二个元素改为 "CS02"
PropertyUtils.setIndexedProperty(course, "codes[1]", "CS02");

4.3 映射属性

指 Map 类型的属性。通过 Key 更新值

Student student = new Student();
student.setName("Joe");

// 学号 "ST-1" 对应的学生对象
PropertyUtils.setMappedProperty(course, "enrolledStudent(ST-1)", student);

5. 嵌套属性访问

当属性是对象时,访问其内部属性就是嵌套访问。PropertyUtils 一把梭

// 传统方式
String name = course.getEnrolledStudent("ST-1").getName();

// BeanUtils 方式
String nameValue = (String) PropertyUtils.getNestedProperty(
    course, 
    "enrolledStudent(ST-1).name"
);

语法规则:用 . 分隔层级,Map 的 Key 用 () 包裹


6. 复制 Bean 属性

6.1 全量复制

BeanUtils.copyProperties() 实现属性复制(同名属性才复制):

// 源对象
Course course = new Course();
course.setName("Computer Science");
course.setCodes(Arrays.asList("CS"));
course.setEnrolledStudent("ST-1", new Student());

// 目标对象(属性名不同不会复制)
CourseEntity courseEntity = new CourseEntity();
BeanUtils.copyProperties(courseEntity, course);

关键限制: ❌ CourseenrolledStudent 不会复制到 CourseEntity(因为后者属性名是 students

6.2 复制时忽略 null 字段

自定义 BeanUtilsBean 重写 copyProperty() 方法:

public class IgnoreNullBeanUtilsBean extends BeanUtilsBean {
    @Override
    public void copyProperty(Object dest, String name, Object value) 
        throws IllegalAccessException, InvocationTargetException {
        if (value != null) {
            super.copyProperty(dest, name, value);
        }
    }
}

测试用例

// 源对象(name 为 null)
Course originalCourse = new Course();
originalCourse.setName(null);
originalCourse.setCodes(Arrays.asList("CS"));

// 目标对象(已有值)
CourseEntity destCourse = new CourseEntity();
destCourse.setName("entityName");

// 执行复制(忽略 null)
IgnoreNullBeanUtilsBean bean = new IgnoreNullBeanUtilsBean();
bean.copyProperties(destCourse, originalCourse);

// 验证结果
assertEquals("entityName", destCourse.getName());  // 未被覆盖
assertThat(destCourse.getCodes()).containsExactly("CS");  // 非null值被复制

7. 总结

BeanUtils 的核心价值

  1. 简化反射操作:用方法调用替代复杂的反射代码
  2. 统一属性访问:简单/索引/映射属性一套 API 搞定
  3. 高效复制:一行代码完成属性拷贝(支持自定义过滤)

适用场景

  • 动态配置加载
  • DTO/VO 转换
  • 框架层属性操作

替代方案对比

  • 原生反射:功能强但代码冗长
  • Spring BeanWrapper:需依赖 Spring 框架
  • PropertyUtils vs BeanUtils:前者类型安全,后者自动类型转换(可能踩坑)

对于复杂场景,建议结合类型转换器(ConvertUtils)使用,避免运行时类型错误。


原始标题:Apache Commons BeanUtils | Baeldung

» 下一篇: Spring框架介绍