Backend Development 6 min read

Why Spring BeanUtils.copyProperties Is Discouraged and MapStruct Is Preferred for Java Object Mapping

The article explains the limitations of Spring's BeanUtils.copyProperties—such as type mismatches, null overwriting, and reflection overhead—and demonstrates how MapStruct provides a faster, compile‑time generated alternative for copying properties between Java objects, with usage examples and Maven setup.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Why Spring BeanUtils.copyProperties Is Discouraged and MapStruct Is Preferred for Java Object Mapping

In typical business development, developers often need to copy properties between BO, PO, DTO objects, and many resort to Spring's BeanUtils.copyProperties to reduce manual getter/setter code.

However, this approach has several drawbacks:

Inconsistent property types cause copy failures (e.g., Long vs String IDs).

Primitive vs wrapper mismatches can trigger exceptions, especially with boolean fields named with an is prefix.

Null values overwrite existing data , leading to data loss when the source contains nulls.

Reflection‑based copying incurs performance penalties.

Incorrect imports may cause copy anomalies.

MapStruct is introduced as a high‑performance alternative because it generates mapping code at compile time, eliminating reflection overhead and running significantly faster than BeanUtils.

Why MapStruct Is Faster

Avoids reflection operations.

Uses pre‑compiled, efficient code.

MapStruct generates the mapping implementation during project build, for example:

@Generated(value = "org.mapstruct.ap.MappingProcessor",
    date = "2024-02-07T00:50:05+0800",
    comments = "version: 1.4.2.Final, compiler: javac, environment: Java 1.8.0_211 (Oracle Corporation)")
public class XXXDTOConverterImpl implements XXXDTOConverter {

    @Override
    public XXXBO convertDtoToBo(XXXDTO XXXDTO) {
        if ( XXXDTO == null ) {
            return null;
        }

        XXXBO XXXBO = new XXXBO();
        XXXBO.setId( XXXDTO.getId() );
        XXXBO.setLabelName( XXXDTO.getLabelName() );
        XXXBO.setSortNum( XXXDTO.getSortNum() );
        XXXBO.setCategoryId( XXXDTO.getCategoryId() );

        return XXXBO;
    }
}

How to Use MapStruct

Add Maven dependencies:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.4.2.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.4.2.Final</version>
</dependency>

Define DTO and BO classes (both implementing Serializable ) and a mapper interface:

@Data
public class SubjectLabelDTO implements Serializable {
    private String labelName;
}

@Data
public class SubjectLabelBO implements Serializable {
    private String labelName;
}
@Mapper
public interface SubjectLabelDTOConverter {
    SubjectLabelDTOConverter INSTANCE = Mappers.getMapper(SubjectLabelDTOConverter.class);
    SubjectLabelBO convertDtoToBo(SubjectLabelDTO subjectLabelDTO);
}

Use the mapper in service code:

public Result
add( SubjectLabelDTO subjectLabelDTO ) {
    SubjectLabelBO subjectLabelBO = SubjectLabelDTOConverter.INSTANCE.convertDtoToBo(subjectLabelDTO);
    // further processing
}

Summary

MapStruct also performs shallow copies; for deep copies you need additional converters. When used together with Lombok, ensure Lombok’s dependency is declared after MapStruct to avoid null‑value issues during generated copying.

JavaPerformanceSpringBeanUtilsMapStructObject Mapping
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login 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.