When to Prefer Spring BeanUtils Over Apache BeanUtils for Object Copying
This article explains the difference between shallow and deep copying in Java, compares Apache BeanUtils and Spring BeanUtils with code examples, highlights performance and safety concerns, and recommends using Spring BeanUtils or alternative libraries for efficient property transfer between objects.
Introduction
In real‑world Java projects we often need to copy properties from one object instance to another (for example, from a data object DO to a DTO) without modifying the source. Writing manual get/set code is tedious, so developers use copy utilities such as Apache BeanUtils, Spring BeanUtils, Dozer, Orika, etc.
Object Copy Basics
Java distinguishes between primitive types and reference types. Assigning a primitive copies its value; assigning a reference copies the pointer, so both variables refer to the same object.
Shallow copy copies only primitive fields and copies references for object fields, leaving both source and target to share the same nested objects. Deep copy creates new instances for referenced objects and copies their fields as well.
Shallow copy: value copy for primitives, reference copy for objects.
Deep copy: value copy for primitives, new object creation and field copy for referenced objects.
Apache BeanUtils
A simple example using Apache BeanUtils:
public class PersonSource {
private Integer id;
private String username;
private String password;
private Integer age;
// getters/setters omitted
}
public class PersonDest {
private Integer id;
private String username;
private Integer age;
// getters/setters omitted
}
public class TestApacheBeanUtils {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
PersonSource personSource = new PersonSource(1, "pjmike", "12345", 21);
PersonDest personDest = new PersonDest();
BeanUtils.copyProperties(personDest, personSource);
System.out.println("persondest: " + personDest);
}
}
// Output: persondest: PersonDest{id=1, username='pjmike', age=21}The core method is:
public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
BeanUtilsBean.getInstance().copyProperties(dest, orig);
}By default org.apache.commons.beanutils.BeanUtils performs a shallow copy. Its implementation adds many checks (type conversion, accessibility, etc.), which makes it considerably slower. Alibaba’s Java coding guidelines explicitly advise against using Apache BeanUtils for property copying.
Ali‑Check | Avoid using Apache Beanutils for property copy.
The actual source code of Apache BeanUtils’ copyProperties method includes validation, handling of DynaBean, Map, and standard JavaBeans, and extensive reflection logic, contributing to its poor performance.
Spring BeanUtils
Spring provides a much simpler implementation:
public class TestSpringBeanUtils {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
PersonSource personSource = new PersonSource(1, "pjmike", "12345", 21);
PersonDest personDest = new PersonDest();
BeanUtils.copyProperties(personSource, personDest);
System.out.println("persondest: " + personDest);
}
}Spring’s copyProperties method essentially copies matching property names via straightforward getter/setter calls, checking only accessibility. The implementation looks like:
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
@Nullable String... ignoreProperties) throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
} catch (Throwable ex) {
throw new FatalBeanException("Could not copy property '" + targetPd.getName() +
"' from source to target", ex);
}
}
}
}
}
}This approach safely copies properties with the same name and compatible types, ignoring missing or ignored fields, and avoids the heavy reflection overhead of Apache BeanUtils.
Conclusion
Apache BeanUtils suffers from poor performance and is discouraged by industry guidelines. Spring BeanUtils offers a lightweight, safe alternative, and other high‑performance copy frameworks such as cglib BeanCopier or Orika (based on Javassist) are also worth considering for large‑scale projects.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
