Backend Development 13 min read

Why MapStruct Is the Fastest Java Bean Mapper and How to Use It

This article introduces MapStruct, a compile‑time Java bean‑mapping framework, explains why it outperforms other mappers, shows how to configure Maven, define mapper interfaces, handle custom field mappings, and demonstrates its impressive runtime performance with practical code examples.

macrozheng
macrozheng
macrozheng
Why MapStruct Is the Fastest Java Bean Mapper and How to Use It

Why Use MapStruct?

In layered software architectures, different layers often use distinct data models such as DO, DTO, and VO, requiring frequent object‑to‑object conversion. Writing manual getter/setter mappings is verbose and error‑prone, which is why developers look for automatic mapping tools.

Using MapStruct

MapStruct (https://mapstruct.org/) is a compile‑time code generator that creates type‑safe, fast mapping implementations based on the "convention over configuration" principle.

Convention over configuration, also known as programming by convention, reduces the number of decisions developers must make while retaining flexibility.

Assume we have two classes that need conversion:

<code>public class PersonDO {
    private Integer id;
    private String name;
    private int age;
    private Date birthday;
    private String gender;
}

public class PersonDTO {
    private String userName;
    private Integer age;
    private Date birthday;
    private Gender gender;
}</code>

First, add the MapStruct dependency and configure the Maven compiler plugin to use the MapStruct processor:

<code>&lt;properties&gt;
    &lt;org.mapstruct.version&gt;1.3.1.Final&lt;/org.mapstruct.version&gt;
&lt;/properties&gt;

&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.mapstruct&lt;/groupId&gt;
        &lt;artifactId&gt;mapstruct&lt;/artifactId&gt;
        &lt;version&gt;${org.mapstruct.version}&lt;/version&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;

&lt;build&gt;
    &lt;plugins&gt;
        &lt;plugin&gt;
            &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
            &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
            &lt;version&gt;3.8.1&lt;/version&gt;
            &lt;configuration&gt;
                &lt;source&gt;1.8&lt;/source&gt;
                &lt;target&gt;1.8&lt;/target&gt;
                &lt;annotationProcessorPaths&gt;
                    &lt;path&gt;
                        &lt;groupId&gt;org.mapstruct&lt;/groupId&gt;
                        &lt;artifactId&gt;mapstruct-processor&lt;/artifactId&gt;
                        &lt;version&gt;${org.mapstruct.version}&lt;/version&gt;
                    &lt;/path&gt;
                &lt;/annotationProcessorPaths&gt;
            &lt;/configuration&gt;
        &lt;/plugin&gt;
    &lt;/plugins&gt;
&lt;/build&gt;</code>

Define a mapper interface:

<code>@Mapper
public interface PersonConverter {
    PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);

    @Mappings({
        @Mapping(source = "name", target = "userName")
    })
    PersonDTO do2dto(PersonDO person);
}</code>

Test the mapper:

<code>public static void main(String[] args) {
    PersonDO personDO = new PersonDO();
    personDO.setName("Hollis");
    personDO.setAge(26);
    personDO.setBirthday(new Date());
    personDO.setId(1);
    personDO.setGender(Gender.MALE.name());

    PersonDTO personDTO = PersonConverter.INSTANCE.do2dto(personDO);
    System.out.println(personDTO);
}</code>

The output shows that the fields are correctly mapped:

<code>PersonDTO{userName='Hollis', age=26, birthday=Sat Aug 08 00:00:44 CST 2020, gender=MALE}</code>

Handling Special Mappings

If source and target property names differ, use

@Mapping(source = "name", target = "userName")

. MapStruct can also map differing types automatically for primitives, their wrappers,

String

, and enums.

For constant values, use the

constant

attribute:

<code>@Mapping(source = "name", constant = "hollis")</code>

When a field requires custom conversion (e.g., a

String

address to a

HomeAddress

object), define a default method in the mapper and reference it with an expression:

<code>@Mapping(target = "address", expression = "java(homeAddressToString(dto.getAddress()))")
default String homeAddressToString(HomeAddress address) {
    return JSON.toJSONString(address);
}</code>

MapStruct also supports built‑in date formatting:

<code>@Mapping(target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")</code>

Performance

Benchmarking shows that mapping 1,000; 10,000; 100,000; and 1,000,000 objects takes 0 ms, 1 ms, 3 ms, and 6 ms respectively, far faster than many alternative libraries.

The speed comes from compile‑time generation of plain Java code, eliminating reflection and allowing the JVM to inline the mapping logic. Errors are also caught at compile time, forcing developers to fix mapping issues early.

Conclusion

MapStruct is a lightweight, compile‑time Java bean‑mapping framework that dramatically reduces boilerplate, handles complex field conversions, and delivers near‑zero runtime overhead. It is especially useful in backend services where DTO‑DO‑VO conversions are frequent.

JavaPerformancecode generationMapStructBean Mapping
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

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.