MapStruct Plus Eliminates the Mapper Interface, Leaving Only an Annotation
Follow my public account: [编程朝花夕拾] to get first-release content.
01 Introduction
In daily development, we often encounter the problem of object conversion. Especially after Alibaba proposed the concepts of DTO, BO, VO, and PO, the scenarios for conversion have increased even more.
There are quite a few conversion tools available, such as BeanUtils, MapStruct, Orika, and others, each with different choices made by individuals or teams.
02 Three Major Schools
The matter of "object-to-object" conversion in the Java ecosystem roughly falls into three schools.
2.1 The Manual School
new an object, then assign values one by one using setter. This is the simplest and most common method. However, if there are many fields, it feels very tedious.
UserDTO dto = new UserDTO();
dto.setId(user.getId());
dto.setName(user.getName());
dto.setAge(user.getAge());
dto.setEmail(user.getEmail());
dto.setPhone(user.getPhone());
dto.setCreateTime(user.getCreateTime());
// ... 30 lines of setters
return dto;
But in the AI era, AI suggestion tools help us directly associate relationships, saving a lot of effort, but the amount of code remains unchanged.
Pros and Cons:
- Pros: Maximum performance, acceptable readability.
- Cons: When fields exceed 10, you start questioning your life; changing a field name requires linked modifications.
2.2 The Reflection School
Representative reflection utility classes: Spring BeanUtils, Apache Commons BeanUtils, Hutool BeanUtil.
BeanUtils.copyProperties(source, target);
Using utility classes is the most effortless, but different tools have slightly different parameters and final structures. If field types are inconsistent, conversion failures may occur. Most critically, performance is the worst.
Pros and Cons:
- Pros: One line of code, a lazy person's gospel.
- Cons: Runtime reflection, performance is a fatal flaw; also lacks type checking, an
intfield and aStringfield with the same name can be "successfully" converted—you won't even know how the data went wrong.
2.3 The Compile-Time Generation School
Representatives: MapStruct, MapStruct Plus.
This means during your javac compilation, APT (Annotation Processing Tool) automatically generates conversion code, with no reflection at runtime. The runtime effect is the same as the Setter method.
This is also the key tool we are introducing today.
03 MapStruct
3.1 Introduction
MapStruct is the de facto standard for Bean mapping in the Java world, under the Apache-2.0 license, with 7.7k stars on GitHub, maintained by an entire organization. The latest stable version is 1.6.3.
Official Website: https://mapstruct.org
GitHub Address: https://github.com/mapstruct/mapstruct
3.2 Usage
Usage is also very simple.
Maven Dependency
...
<properties>
<org.mapstruct.version>1.6.3</org.mapstruct.version>
</properties>
...
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
...
Define Conversion Interface
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface UserMapper {
UserVO userDtoToVO(UserDTO userDto);
}
Here, using the Spring instance mode, you can customize the mapping of different fields on the defined methods via @Mapping(target = "a", source = "b").
Configure the annotation processor in Maven, and during compilation, it generates a UserMapperImpl.java for you, filled entirely with proper target.setXxx(source.getXxx()).
Invocation
3.3 Disadvantages
The biggest problem with this thing: annoying.
For every pair of type conversions, you have to write a @Mapper interface. If your project has 50 DTOs, that's 50 Converter classes. A direct class explosion!
04 MapStruct Plus
A domestic "plug-in" has arrived, which, as the name suggests, is an enhanced version of MapStruct.
4.1 Introduction
Mapstruct Plus is an enhancement tool for Mapstruct. Based on Mapstruct, it implements the function of automatically generating Mapper interfaces and strengthens some features, making Java type conversion more convenient and elegant.
It might be the simplest and most powerful Java Bean conversion tool.
The pain point solved by Mapstruct Plus is precisely the elimination of defining conversion interfaces, replaced by annotations.
Official Website: https://www.mapstruct.plus/
GitHub Address: https://github.com/linpeilie/mapstruct-plus
4.2 Usage
Maven Dependency
<properties>
<mapstruct-plus.version>latest version</mapstruct-plus.version>
</properties>
<dependencies>
<dependency>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
<version>${mapstruct-plus.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-processor</artifactId>
<version>${mapstruct-plus.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
Define Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@AutoMapper(target = UserVO.class)
public class UserDTO {
/** User ID */
private Integer id;
/** Username */
private String name;
/** Date of Birth */
private Date birthday;
/** Phone Number */
private String phone;
}
The most critical annotation here is @AutoMapper(target = UserVO.class)
Invocation
It's that simple, a single annotation achieves mutual conversion. Let's see why: it turns out that during compilation, implementation classes for mutual conversion are generated.
4.3 Advantages
- Zero interfaces, zero templates—this is the core selling point, CRUD business code writes like a breeze.
- The underlying layer is still MapStruct—compile-time generation, zero reflection, type safety, all inherited.
- Bidirectional conversion auto-generated—both A→B and B→A are generated for you, no need to write two copies.
- Bilingual Chinese-English documentation—this is genuinely appealing for domestic developers, the author is a native Chinese speaker, and the documentation is written more down-to-earth than MapStruct's official docs.
4.4 Disadvantages
Since it is an enhancement tool for another, it inevitably depends on MapStruct.
- Small community size—the number of Issues/Discussions is far less than MapStruct, encountering niche problems means you have to dig through the source code yourself.
- Version coupling with the underlying layer—when MapStruct has a major version upgrade, Plus must follow suit, introducing a certain degree of uncertainty.
- Slightly weaker flexibility in complex custom scenarios—for example, advanced features like
expression, Decorator, SPI, Plus is still catching up with the original.
05 Bean Conversion Tool Collection
Let's organize the commonly used conversion tools:
5.1 Compile-Time School
- MapStruct: Industry benchmark, authoritative.
- MapStruct Plus: The "lazy version" of MapStruct, the top choice domestically.
- Selma / JMapper: Historical solutions, no longer active, not recommended.
5.2 Reflection School
- Spring BeanUtils: Comes with Spring, fine for unit tests and small utilities, but don't use it for large objects in production.
- Apache Commons BeanUtils: Slow enough to make you question your life, not recommended.
- Hutool BeanUtil: A domestic utility package, the top choice for domestic projects, but essentially still reflection.
- Dozer: XML configuration mapping, once popular, now basically dead.
- Orika: Based on bytecode generation, faster than reflection, slower than compile-time, stuck in the middle.
5.3 Bytecode School
- Cglib BeanCopier: Favored internally by Alibaba and Meituan, cache after the first creation, performance close to handwritten.
- ByteBuddy: A modern solution, even used by Spring itself.
06 Summary
Choosing a tool is not about choosing the strongest, but the most suitable. Every tool has its necessary use cases, and complex business logic can never be separated from handwritten code as a fallback.