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 类:Course
和 Student
。注意:属性命名要符合规范(比如 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);
关键限制:
❌ Course
的 enrolledStudent
不会复制到 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 的核心价值:
- 简化反射操作:用方法调用替代复杂的反射代码
- 统一属性访问:简单/索引/映射属性一套 API 搞定
- 高效复制:一行代码完成属性拷贝(支持自定义过滤)
适用场景:
- 动态配置加载
- DTO/VO 转换
- 框架层属性操作
替代方案对比:
- 原生反射:功能强但代码冗长
- Spring BeanWrapper:需依赖 Spring 框架
- PropertyUtils vs BeanUtils:前者类型安全,后者自动类型转换(可能踩坑)
对于复杂场景,建议结合类型转换器(
ConvertUtils
)使用,避免运行时类型错误。