1. Overview

Apache Commons BeansUtils contains all tools necessary for working with Java beans.

Simply put, a bean is a simple Java class containing fields, getters/setters, and a no-argument constructor.

Java provides reflection and introspection capabilities to identify getter-setter methods and call them dynamically. However, these APIs can be difficult to learn and may require developers to write boilerplate code to perform simple operations.

In this article, we will explore some of the main usages of Apache Commons BeansUtils while dealing with Java Beans.

2. Maven Dependencies

Here is the Maven dependency required to be included in the POM file before using it:

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

3. Creating a Java Bean

Let’s create two bean classes Course and Student with typical getter and setter methods.

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

    //  standard getters/setters
}
public class Student {
    private String name;

    //  standard getters/setters
}

We have a Course class that has a course name, course codes, and multiple enrolled students. Enrolled Students are identified by unique enrollment Id. Course class maintains enrolled students in a Map object where enrollment Id is a key, and the student object will be the value.

4. Property Access

Bean properties can be divided into three categories: simple properties, indexed properties, and mapped properties.

4.1. Simple Property

Single-value properties are also called simple or scalar.

Their value might be a primitive such as int and float, or complex type objects. BeanUtils has a PropertyUtils class that allows us to modify simple properties in a Java Bean.

Here is the example code to set the properties:

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. Indexed Property

Indexed properties have a collection as a value that can be individually accessed using an index number. As an extension to JavaBean, BeanUtils considers java.util.List type values as indexed as well.

We can modify an indexed property individual value using a PropertyUtils’s setIndexedProperty method.

Here is example code modifying indexed property:

PropertyUtils.setIndexedProperty(course, "codes[1]", "CS02");

4.3. Mapped Property

Any property that has a java.util.Map as the underlying type is called a mapped property. BeanUtils allows us to update the individual value in a map using a String-valued key.

Here is the example code to modify the value in a mapped property:

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

PropertyUtils.setMappedProperty(course, "enrolledStudent(ST-1)", student);

5. Nested Property Access

If a property value is an object and we need to access a property value inside that object, that would be accessing a nested property. PropertyUtils allow us to access and modify nested properties as well.

Assume we want to access the name property of Student class through Course object. We might write:

String name = course.getEnrolledStudent("ST-1").getName();

We can access the nested property values using getNestedProperty and modify the nested property using setNestedProperty methods in PropertyUtils. Here is the code:

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

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

6. Copy Bean Properties

Copying properties from one object to another object is often tedious and error-prone for developers.

6.1. Copy All Properties

BeanUtils class provides a copyProperties method that copies the properties of source object to target object where the property name is same in both objects.

Let’s create another bean class. It’ll have the same properties as Course except a students property instead of the enrolledStudent Course property. Let’s name that class CourseEntity. The class would look like:

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

    //  standard getters/setters
}

Now, we’ll copy the properties of Course object to CourseEntity object:

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);

Remember this will copy the properties with the same name only. Therefore, it will not copy the property enrolledStudent in Course class because there is no property with the same name in CourseEntity class.

6.2. Ignore Null Fields During Copy

Sometimes, it can be handy to copy only non-null fields. *To do so, we can define our own BeanUtilsBean and override the definition of 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);
        }
    }
}

As we can see, we copy the property only if its initial value isn’t null. Let’s now test this code:

Course originalCourse = new Course();
originalCourse.setName(null);
originalCourse.setCodes(Arrays.asList("CS"));
originalCourse.setEnrolledStudent("ST-1", new Student());

CourseEntity destCourse = new CourseEntity();
destCourse.setName("entityName");

IgnoreNullBeanUtilsBean ignoreNullBeanUtilsBean = new IgnoreNullBeanUtilsBean();
ignoreNullBeanUtilsBean.copyProperties(destCourse, originalCourse);
        
assertEquals("entityName", destCourse.getName());
assertThat(destCourse.getCodes()).containsExactly("CS");
assertThat(destCourse.getStudents()).isEmpty();

We instantiated a Course object with a null name. Then, we created a CourseEntity object with the name entityName. After this, we copied all non-null properties from the Course object to their corresponding property in the CourseEntity object. As we expected, we could finally check that the name of our CourseEntity object didn’t change. In a word, the null name in the Course object got ignored!

7. Conclusion

In this quick article, we went over the utility classes provided by BeanUtils. We also looked into different types of properties and how can we access and modify their values.

Finally, we looked into accessing nested property values and copying properties from one object to another object.

Of course, reflection and introspection capabilities in the Java SDK also allow us to access properties dynamically but it can be difficult to learn and require some boilerplate code. BeanUtils allows us to access and modify these values with a single method call.

Code snippets can be found over on GitHub.


« 上一篇: Apache Commons Text简介
» 下一篇: Spring框架介绍