Why Bean Property Copy Tools Can Fail: Pitfalls, Performance, and Safer Alternatives

This article examines the hidden risks and performance drawbacks of Java bean property copy utilities such as Spring BeanUtils, CGLIB BeanCopier, and MapStruct, demonstrates concrete examples of type‑conversion errors, and recommends custom converters or IDE‑generated mapping code for reliable data transfer.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Why Bean Property Copy Tools Can Fail: Pitfalls, Performance, and Safer Alternatives

1 Background

In a previous column the author warned against using generic property‑copy tools and suggested defining explicit conversion classes, optionally auto‑filled by an IDEA plugin.

The main reasons for avoiding such tools are poor performance, occasional bugs, and hidden runtime hazards.

2 Example

A real‑world case from the author's company showed that commons-beanutils performed poorly when copying bean properties, while switching to Spring's BeanUtils improved speed dramatically. The article does not provide a benchmark but encourages readers to test themselves.

Running the following Spring BeanUtils.copyProperties example triggers a ClassCastException because the source list holds Integer values while the target expects String values:

import lombok.Data;
import java.util.List;

@Data
public class A {
    private String name;
    private List<Integer> ids;
}

@Data
public class B {
    private String name;
    private List<String> ids;
}

import org.springframework.beans.BeanUtils;
import java.util.Arrays;

public class BeanUtilDemo {
    public static void main(String[] args) {
        A first = new A();
        first.setName("demo");
        first.setIds(Arrays.asList(1, 2, 3));

        B second = new B();
        BeanUtils.copyProperties(first, second);
        for (String each : second.getIds()) { // ClassCastException
            System.out.println(each);
        }
    }
}

Debugging shows that after copying, second.getIds() still contains Integer objects, causing the exception when iterated as String.

The same issue appears with CGLIB's BeanCopier when no custom converter is supplied:

import org.easymock.cglib.beans.BeanCopier;
import java.util.Arrays;

public class BeanUtilDemo {
    public static void main(String[] args) {
        A first = new A();
        first.setName("demo");
        first.setIds(Arrays.asList(1, 2, 3));

        B second = new B();
        BeanCopier beanCopier = BeanCopier.create(A.class, B.class, false);
        beanCopier.copy(first, second, null);
        for (String each : second.getIds()) { // ClassCastException
            System.out.println(each);
        }
    }
}

Both tools expose the generic‑type erasure problem: at runtime List<Integer> and List<String> are both plain List, so the mismatch is not caught at compile time.

MapStruct, a compile‑time code generator, can handle the conversion correctly. The following mapper and usage illustrate automatic conversion from List<Integer> to List<String>:

import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface Converter {
    Converter INSTANCE = Mappers.getMapper(Converter.class);
    B aToB(A car);
}
import java.util.Arrays;

public class BeanUtilDemo {
    public static void main(String[] args) {
        A first = new A();
        first.setName("demo");
        first.setIds(Arrays.asList(1, 2, 3));

        B second = Converter.INSTANCE.aToB(first);
        for (String each : second.getIds()) { // works fine
            System.out.println(each);
        }
    }
}

The generated implementation converts each Integer to its String representation, hiding the type difference from the developer.

If additional fields have mismatched types (e.g., String number in A and Long number in B), MapStruct will generate code that attempts Long.parseLong, which throws NumberFormatException when the source string is not numeric.

CGLIB, by default, simply copies matching fields and leaves the mismatched number as null in the target.

A manual converter written with an IDE plugin can make the mismatch obvious at compile time:

public final class A2BConverter {
    public static B from(A first) {
        B b = new B();
        b.setName(first.getName());
        b.setIds(first.getIds());
        return b;
    }
}

Because the conversion logic is explicit, type errors are caught early during development.

IDE warning screenshot
IDE warning screenshot

3 Conclusion

Java generics are checked only at compile time; after type erasure, List<Integer> and List<String> are both plain List, allowing unsafe assignments that surface as runtime exceptions when using generic property‑mapping tools.

MapStruct leverages annotation processing to read generic types during compilation, enabling correct mapping but also silently performing conversions that may introduce side effects if the developer is unaware of the type change.

A simple performance comparison (not reproduced here) showed that custom, IDE‑generated conversion code is both fast and safe, while generic tools add overhead and hidden risks.

Therefore, use property‑copy utilities cautiously; whenever possible, define explicit converters or let an IDE generate the boilerplate, which provides compile‑time type safety and high execution efficiency.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaperformanceBeanUtilsmapstructType SafetycglibProperty Mapping
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.