Backend Development 15 min read

Using MapStruct for Java Bean Mapping: Concepts, Code Samples, and Configuration

This article introduces MapStruct, explains its purpose as a compile‑time Java bean mapper, walks through a complete example with Maven dependencies, entity and DTO classes, mapper interfaces, custom mappings, and configuration options, and compares its performance with other copying libraries.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Using MapStruct for Java Bean Mapping: Concepts, Code Samples, and Configuration

Hello everyone, I'm Chen. This article starts from the concept of MapStruct and uses concrete code examples to explore its usage, finally comparing it with other tools available on the market.

Official Introduction

MapStruct is a code generator that greatly simplifies the implementation of mappings between JavaBean types by following a convention‑over‑configuration approach. The generated mapping code uses plain method calls, making it fast, type‑safe, and easy to understand.

Why use it? Multi‑layer applications often need to map between different object models (e.g., entities and DTOs). Writing such mapping code is tedious and error‑prone; MapStruct automates this work as much as possible.

Unlike other mapping frameworks, MapStruct generates the bean mapping at compile time, ensuring high performance, quick developer feedback, and thorough error checking.

How does it work? MapStruct is an annotation processor inserted into the Java compiler and can be used in command‑line builds (Maven, Gradle) or directly in IDEs. It provides sensible defaults but allows custom implementations when special behavior is needed.

In short, MapStruct is a tool for entity class mapping – essentially copying objects.

Simple Implementation

We examine a simple example from the official site.

1. Add dependencies

<dependency>
  <groupId>org.mapstruct</groupId>
  <artifactId>mapstruct-jdk8</artifactId>
  <version>1.3.0.Final</version>
</dependency>
!-- annotation processor for automatic mapper implementation -->
<dependency>
  <groupId>org.mapstruct</groupId>
  <artifactId>mapstruct-processor</artifactId>
  <version>1.2.0.Final</version>
</dependency>

During compilation we may encounter an error like java: No property named "numberOfSeats" exists in source parameter(s). Did you mean "null"? ; the cause is a version mismatch between mapstruct-processor and Lombok . Align them (e.g., mapstruct-processor: 1.2.0.Final and Lombok: 1.16.14 ).

2. Prepare entity and DTO classes

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Car {
    private String make;
    private int numberOfSeats;
    private CarType type;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CarDto {
    private String make;
    private int seatCount;
    private String type;
}

3. Create mapper interface

@Mapper
public interface CarMapper {
    CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);

    @Mapping(source = "numberOfSeats", target = "seatCount")
    CarDto carToCarDto(Car car);
}

Analysis

@Mapper marks the interface for MapStruct processing at compile time.

The method carToCarDto takes a Car and returns a CarDto ; the method name is arbitrary.

When source and target property names differ, @Mapping configures the name conversion.

Multiple mapping methods in one interface result in multiple generated implementations.

The implementation instance can be obtained via Mappers.getMapper .

4. Generated implementation class

After compilation, a CarMapperImpl.class file is generated in the target directory, containing the automatically generated set/get code and special handling for enums.

5. Client code

@Test
public void shouldMapCarToDto() {
    Car car = new Car("Morris", 5, CarType.SEDAN);
    CarDto carDto = CarMapper.INSTANCE.carToCarDto(car);
    System.out.println(carDto);
}

The test prints the mapped DTO.

MapStruct Configuration

@Mapper

The @Mapper annotation can specify componentModel with four possible values:

default : No component model; obtain the mapper via Mappers.getMapper .

cdi : Generates an application‑scoped CDI bean, injectable with @Inject .

spring : Generates a Spring bean, injectable with @Autowired .

jsr330 : Generates a bean annotated with @Named and @Singleton , injectable with @Inject .

In the examples we use the default model, but you can also use @Autowired to inject the mapper.

The uses attribute allows custom conversion classes. Example:

@Mapper(uses = {BooleanStrFormat.class})
public interface CarMapper { ... }

/** Custom conversion class */
@Component
public class BooleanStrFormat {
    public String toStr(boolean type) { return type ? "Y" : "N"; }
    public boolean toBoolean(String type) { return "Y".equals(type); }
}

@Mapping

@Mapping configures how a bean property or enum constant is mapped. By default, properties with the same name are mapped automatically. You can manually specify source , target , expression , constant , etc.

target : Name of the target property.

source : Name of the source property (can be qualified with parameter name for multi‑parameter methods).

dateFormat , numberFormat : Format conversions.

constant : Assign a constant value to the target.

expression : Use a Java expression for the target value.

ignore : Skip the field.

Example using expression instead of uses :

@Mapping(target = "type", expression = "java(new com.ittest.controller.BooleanStrFormat().toStr(carVo.isType()))")
CarDto carVoToDtoWithExpression(CarVo carVo);

@Mappings

Multiple @Mapping annotations can be grouped:

@Mappings({
    @Mapping(source = "id", target = "carId"),
    @Mapping(source = "name", target = "carName"),
    @Mapping(source = "color", target = "carColor")
})

@MappingTarget

Used to update an existing object:

void updateBwmCar(Car car, @MappingTarget BMWCar bwmCar);

The generated implementation copies matching fields from car into the existing bwmCar instance.

Deep Copy vs Shallow Copy

Deep copy creates a new object with its own state, while shallow copy only copies references. MapStruct creates new objects, therefore it performs deep copies.

MapStruct vs Other Copy Utilities

Performance tests (referencing ZhaoYingChao88) show the following order from fastest to slowest:

Manual Copy > MapStruct >= CglibCopy > SpringBeanUtils > ApachePropertyUtils > ApacheBeanUtils

MapStruct is about ten times faster than Apache BeanUtils, 4‑5 times faster than Spring BeanUtils, and comparable to BeanCopier.

Conclusion: For large data volumes, MapStruct (or BeanCopier) offers significant performance advantages. For small‑scale object copying the choice matters less, but MapStruct remains the most efficient.

Reference link: https://blog.csdn.net/ZYC88888/article/details/109681423?spm=1001.2014.3001.5501

Additional note: The author has opened several technical discussion groups; interested readers can scan the QR code to join.

Finally, please give a like, view, and share.

Javacode generationSpringMapStructMapperBean 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

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.