MapStruct 使用教程
MapStruct 使用教程
MapStruct 是一个用于在 Java Bean 之间执行对象映射的代码生成器。它通过注解处理器在编译时自动生成映射代码,而不是在运行时使用反射技术,从而提供了高性能的映射解决方案。本文将详细介绍 MapStruct 的基本用法、高级特性以及如何在实际项目中集成和使用 MapStruct。
目录
- 什么是 MapStruct
- MapStruct 的优势
- MapStruct 的基本用法
◦ 1. 添加依赖
◦ 2. 定义源和目标 Bean
◦ 3. 创建映射器接口
◦ 4. 使用映射器 - MapStruct 的高级特性
◦ 1. 字段映射
◦ 2. 嵌套对象映射
◦ 3. 集合映射
◦ 4. 自定义映射方法
◦ 5. 使用条件映射
◦ 6. 默认值和常量映射 - 集成 MapStruct 到项目中
◦ 1. Maven 配置
◦ 2. Gradle 配置 - 常见问题与解决方案
- 总结
什么是 MapStruct
MapStruct 是一个基于注解的 Java Bean 映射工具,它在编译时生成类型安全的映射代码,避免了运行时的反射开销。MapStruct 的主要目标是简化对象之间的映射过程,同时保持高性能和可维护性。
MapStruct 的优势
• 编译时类型安全:在编译阶段捕捉类型不匹配的问题,避免运行时错误。
• 高性能:生成的代码避免了反射,性能接近手写映射代码。
• 可定制性强:支持自定义映射方法、表达式和条件映射。
• 易于集成:简单的注解配置,无缝集成到现有项目中。
• 良好的可测试性:生成的映射器是普通的 Java 接口,易于进行单元测试。
MapStruct 的基本用法
1. 添加依赖
以 Maven 项目为例,需要在 pom.xml
文件中添加 MapStruct 的依赖及注解处理器。
<dependencies>
<!-- MapStruct 核心库 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
<annotationProcessorPaths>
<!-- MapStruct 注解处理器 -->
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
注意:确保 <source>
和 <target>
版本与项目使用的 Java 版本一致。如果使用其他构建工具(如 Gradle),请参考 MapStruct 官方文档 进行相应配置。
2. 定义源和目标 Bean
假设我们有两个类 SourceBean
和 TargetBean
,需要将 SourceBean
的属性映射到 TargetBean
。
// SourceBean.java
public class SourceBean {
private String firstName;
private String lastName;
private int age;
private Address sourceAddress;
// getters and setters
}
// TargetBean.java
public class TargetBean {
private String fullName;
private int age;
private Address targetAddress;
// getters and setters
}
// Address.java
public class Address {
private String street;
private String city;
// getters and setters
}
3. 创建映射器接口
使用 @Mapper
注解定义一个映射器接口,并在需要的地方使用 @Mapping
注解指定字段映射规则。
// SourceTargetMapper.java
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper(SourceTargetMapper.class);
@Mapping(source = "firstName", target = "fullName",
expression = "java(sourceBean.getFirstName() + \" \" + sourceBean.getLastName())")
@Mapping(source = "sourceAddress", target = "targetAddress")
TargetBean sourceToTarget(SourceBean sourceBean);
// 如果 Address 类也需要映射,可以定义嵌套的映射方法
Address mapAddress(SourceBean.Address sourceAddress);
}
说明:
• @Mapper
注解标识这是一个 MapStruct 映射器接口。
• INSTANCE
是一个单例实例,用于调用映射方法。
• @Mapping
注解用于指定字段之间的映射关系。如果字段名称不一致,需要使用 source
和 target
属性进行映射。
• expression
属性允许使用 Java 表达式进行复杂的字段转换。
4. 使用映射器
在代码中使用映射器接口进行对象映射。
// App.java
public class App {
public static void main(String[] args) {
SourceBean source = new SourceBean();
source.setFirstName("John");
source.setLastName("Doe");
source.setAge(30);
Address address = new Address();
address.setStreet("123 Main St");
address.setCity("Anytown");
source.setSourceAddress(address);
TargetBean target = SourceTargetMapper.INSTANCE.sourceToTarget(source);
System.out.println(target.getFullName()); // 输出: John Doe
System.out.println(target.getAge()); // 输出: 30
System.out.println(target.getTargetAddress().getStreet()); // 输出: 123 Main St
}
}
MapStruct 的高级特性
1. 字段映射
默认情况下,MapStruct 会根据字段名称自动映射。如果源和目标字段名称不一致,可以使用 @Mapping
注解指定。
@Mapper
public interface AdvancedMapper {
@Mapping(source = "sourceField", target = "targetField")
TargetBean map(SourceBean source);
}
2. 嵌套对象映射
当源和目标对象包含嵌套对象时,需要定义嵌套对象的映射方法。
@Mapper
public interface NestedMapper {
NestedMapper INSTANCE = Mappers.getMapper(NestedMapper.class);
@Mapping(source = "address.street", target = "location.street")
@Mapping(source = "address.city", target = "location.city")
TargetBean map(SourceBean source);
default Location map(Address address) {
if (address == null) return null;
Location loc = new Location();
loc.setStreet(address.getStreet());
loc.setCity(address.getCity());
return loc;
}
}
3. 集合映射
MapStruct 支持集合类型的映射,如 List
、Set
等。
@Mapper
public interface CollectionMapper {
CollectionMapper INSTANCE = Mappers.getMapper(CollectionMapper.class);
List<TargetBean> mapSources(List<SourceBean> sources);
TargetBean map(SourceBean source);
}
注意:集合中的每个元素将逐一映射,需要确保单个元素的映射方法已定义。
4. 自定义映射方法
对于复杂的映射逻辑,可以定义自定义的映射方法。
@Mapper
public interface CustomMapper {
CustomMapper INSTANCE = Mappers.getMapper(CustomMapper.class);
@Mapping(source = "age", target = "ageGroup")
TargetBean map(SourceBean source);
default String mapAgeToAgeGroup(int age) {
if (age < 18) return "Minor";
else if (age < 65) return "Adult";
else return "Senior";
}
}
5. 使用条件映射
可以使用 @Condition
注解或自定义表达式实现条件映射。
@Mapper
public interface ConditionalMapper {
ConditionalMapper INSTANCE = Mappers.getMapper(ConditionalMapper.class);
@Mapping(target = "status",
expression = "java(source.getAge() >= 18 ? \"Adult\" : \"Minor\")")
TargetBean map(SourceBean source);
}
6. 默认值和常量映射
可以为目标字段设置默认值或常量。
@Mapper
public interface DefaultValueMapper {
DefaultValueMapper INSTANCE = Mappers.getMapper(DefaultValueMapper.class);
@Mapping(target = "status", constant = "Active")
TargetBean map(SourceBean source);
}
集成 MapStruct 到项目中
1. Maven 配置
确保在 pom.xml
中正确添加 MapStruct 依赖和注解处理器。
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
2. Gradle 配置
如果使用 Gradle,可以在 build.gradle
中添加如下配置:
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.mapstruct:mapstruct:1.5.5.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
}
// 对于 Gradle 6.0 及以上版本,可以使用以下配置
// 如果使用 Kapt(Kotlin),请相应调整
注意:确保使用的 Gradle 版本支持 annotationProcessor
配置。对于 Kotlin 项目,需要使用 kapt
插件。
常见问题与解决方案
-
映射器接口未被实现:
◦ 确保在构建工具中正确配置了 MapStruct 注解处理器。
◦ 检查@Mapper
注解是否正确应用在接口上。
◦ 确保源和目标类具有相应的 getter 和 setter 方法。 -
字段未映射警告:
◦ 使用@Mapping
注解明确指定所有需要映射的字段。
◦ 使用@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
忽略未映射的目标字段(不推荐,除非确信不需要映射)。 -
复杂类型映射失败:
◦ 确保为复杂类型定义了相应的映射方法。
◦ 使用@Mapper
的uses
属性引用其他映射器接口。@Mapper(uses = AddressMapper.class) public interface ComplexMapper { ComplexMapper INSTANCE = Mappers.getMapper(ComplexMapper.class); @Mapping(source = "address", target = "location") TargetBean map(SourceBean source); } @Mapper public interface AddressMapper { AddressMapper INSTANCE = Mappers.getMapper(AddressMapper.class); Location map(Address address); }
-
泛型类型映射:
◦ MapStruct 支持泛型类型的映射,但需要确保泛型类型参数被正确推断或指定。@Mapper public interface GenericMapper<S, T> { T map(S source); } // 使用时指定具体类型 GenericMapper<SourceBean, TargetBean> specificMapper = Mappers.getMapper(GenericMapper.class);
注意:对于更复杂的泛型映射,可能需要结合其他工具或手动编写部分代码。
总结
MapStruct 是一个功能强大且高效的 Java Bean 映射工具,通过编译时生成映射代码,提供了类型安全和高性能的映射解决方案。其丰富的注解和灵活的自定义能力使得处理复杂映射变得简单。在实际项目中,合理使用 MapStruct 可以显著减少样板代码,提高开发效率和代码质量。
通过本文的介绍,相信您已经掌握了 MapStruct 的基本用法和一些高级特性。建议在实际项目中尝试使用 MapStruct,并参考 MapStruct 官方文档 了解更多详细信息和最佳实践。