Backend Development 17 min read

MapStruct Tutorial: Introduction, Setup, and Advanced Usage

This article introduces MapStruct, explains the challenges of JavaBean conversion, shows how to add dependencies, define POJOs and DTOs, create mapper interfaces with @Mapper, demonstrates basic and advanced mapping scenarios—including custom methods, nested objects, and dependency injection—while highlighting its performance and debugging benefits.

Top Architect
Top Architect
Top Architect
MapStruct Tutorial: Introduction, Setup, and Advanced Usage

1. What is MapStruct

Converting between JavaBeans often causes performance and maintenance issues, especially when using reflection or manually writing converters. MapStruct is an annotation processor that generates type‑safe, high‑performance, dependency‑free mapping code at compile time.

1.1 The JavaBean Conversion Problem

Using reflection (e.g., BeanUtils, BeanCopier) is convenient but incurs runtime overhead and requires matching field names, leading to extensive boilerplate code.

1.2 Changes Brought by MapStruct

MapStruct generates mapping code automatically, offering:

Annotation processing

Generation of JavaBean mapping code

Type safety, high performance, no runtime dependencies

2. Getting Started with MapStruct

2.1 Adding Dependencies

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.20</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-jdk8</artifactId>
    <version>${org.mapstruct.version}</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>${org.mapstruct.version}</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.1.0</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

2.2 POJO Classes

@Data
public class User {
    private Integer id;
    private String name;
    private String address;
    private Date birth;
}

2.3 DTO Classes

@Data
public class UserDto implements Serializable {
    private Integer id;
    private String name;
    private String address;
    private Date birth;
}

2.4 Creating the Mapper Interface

// You can also use an abstract class instead of an interface
@Mapper
public interface UserMapper {
    UserDto userToUserDto(User user);
    List
userToUserDto(List
users);
}

2.5 Test Method

@Test
public void userPoToUserDto() {
    User user = new User();
    user.setId(1);
    user.setName("myx");
    user.setAddress("河北沧州");
    user.setBirth(new Date());
    UserMapper mapper = Mappers.getMapper(UserMapper.class);
    UserDto userDto = mapper.userToUserDto(user);
    System.out.println(userDto);
}

3. Advantages of MapStruct

3.1 High Performance

Unlike reflection, the generated code behaves like hand‑written code, providing fast execution.

3.2 Simple to Use

For fully matching fields, no extra configuration is needed; for special cases (different names, custom conversions) annotations such as @Mapping are used.

3.3 Independent Code

The generated mapper classes have no runtime dependencies.

3.4 Easy Debugging

Since the code is generated, you can step through it directly in a debugger.

4. MapStruct Usage Scenarios

4.1 Same Property Names

If source and target fields share the same name, MapStruct maps them automatically.

4.2 Different Property Names

Use @Mapping to specify source and target fields.

@Mapping(source = "pwd", target = "password")
UserDto userToUserDto(User user);

4.3 Mapping Collections and Nested Objects

MapStruct can map lists and nested beans automatically, or you can define explicit methods for complex cases.

@Mappings({
    @Mapping(source = "pwd", target = "password"),
    @Mapping(source = "subUser", target = "subUserDto")
})
UserDto userToUserDto(User user);

4.4 Custom Conversion Methods

Define default methods in the mapper for conversions that cannot be generated automatically.

default SubUserDto subSource2subTarget(SubUser subUser) {
    if (subUser == null) {
        return null;
    }
    SubUserDto dto = new SubUserDto();
    dto.setResult(!subUser.getDeleted().equals(0));
    dto.setName(subUser.getName() == null ? "" : subUser.getName());
    return dto;
}

4.5 Updating Existing Beans

Use @MappingTarget to modify an existing instance instead of creating a new one.

/**
 * Update target bean; the parameter annotated with @MappingTarget will be modified.
 */
void updateDeliveryAddressFromAddress(SubUser subUser, @MappingTarget NewUserDto newUserDto);

4.6 Mapping Maps

MapStruct can convert maps, e.g., from Map<String, Date> to Map<String, String> with a date format.

@MapMapping(valueDateFormat = "yyyy-MM-dd HH:mm:ss")
Map
DateMapToStringMap(Map
sourceMap);

4.7 Dependency Injection

MapStruct supports Spring, CDI, or other DI frameworks via the componentModel attribute.

@Mapper(componentModel = "spring")
public interface UserMapper { ... }

4.8 Custom Type Conversions

When source and target types differ (e.g., Boolean ↔ String), define a helper class and reference it with the uses attribute.

@Mapper(uses = {BooleanStrFormat.class})
public interface UserMapper {
    UserDto userToNewUserDto(User user);
}

public class BooleanStrFormat {
    public String toStr(Boolean isDisable) {
        return isDisable ? "Y" : "N";
    }
    public Boolean toBoolean(String str) {
        return "Y".equals(str);
    }
}

5. Obtaining a Mapper Instance

5.1 Using Mappers.getMapper

Call Mappers.getMapper(UserMapper.class) to retrieve the implementation.

5.2 Using Dependency Injection

When componentModel is set to "spring" (or "cdi"), the mapper can be injected as a Spring bean.

5.3 Injection Strategies

Choose FIELD (default) or CONSTRUCTOR injection via the injectionStrategy attribute.

@Mapper(componentModel = "cdi", injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface UserMapper { ... }

6. Additional Information

The article also contains promotional messages, QR codes for joining a WeChat group, and links to other resources, which are not part of the technical tutorial.

Javacode generationbackend developmentMapStructAnnotation ProcessorBean Mapping
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.