MapStruct使用说明
基于项目实践经验整理的MapStruct对象转换框架使用指南
📚 目录
一、MapStruct简介
1.1 什么是MapStruct
MapStruct是一个Java注解处理器,用于生成类型安全的Bean映射代码。它在编译时自动生成对象转换代码,避免了运行时反射带来的性能开销。
1.2 核心优势
特性 | 说明 |
---|---|
零运行时反射 | 编译期生成代码,运行时性能接近手写代码 |
类型安全 | 编译时检查字段映射,避免运行时错误 |
易于调试 | 生成的代码清晰可读,便于调试和审查 |
简化维护 | 集中管理映射规则,避免重复代码 |
扩展性强 | 支持自定义转换逻辑、表达式映射等高级特性 |
1.3 适用场景
- Entity ↔ DTO 对象转换
- VO ↔ DTO 数据传输
- PO ↔ BO 业务对象映射
- 多层架构中的对象转换
二、Maven依赖配置
2.1 添加版本属性
在 pom.xml
的 <properties>
标签中添加版本号:
<properties>
<java.version>17</java.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
2.2 添加MapStruct依赖
在 <dependencies>
标签中添加:
<!-- MapStruct对象转换 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
2.3 配置编译器插件
在 <build><plugins>
中配置Maven编译插件(重要):
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
<compilerArgs>
<!-- 可选:全局配置MapStruct默认组件模型 -->
<arg>-Amapstruct.defaultComponentModel=spring</arg>
</compilerArgs>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<!-- 如果项目使用Lombok,必须配置以下两项 -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok-mapstruct-binding.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
注意事项:
mapstruct-processor
是注解处理器,负责在编译时生成实现类lombok-mapstruct-binding
确保Lombok和MapStruct协同工作- 处理器顺序很重要:mapstruct-processor → lombok → lombok-mapstruct-binding
三、基本使用方法
3.1 创建Mapper接口
建议创建专门的包存放Mapper接口,例如:cn.oalo.mapper.convert
package cn.oalo.mapper.convert;
import cn.oalo.dto.DataExportDto;
import cn.oalo.entity.Data;
import org.mapstruct.Mapper;
import java.util.List;
/**
* Data实体与DTO转换Mapper
*/
@Mapper(componentModel = "spring")
public interface DataConvertMapper {
/**
* 单个对象转换
*/
DataExportDto toExportDto(Data data);
/**
* 批量转换(MapStruct会自动实现)
*/
List<DataExportDto> toExportDtoList(List<Data> dataList);
}
关键点说明:
@Mapper
注解标记这是一个MapStruct映射接口componentModel = "spring"
让生成的实现类注册为Spring Bean- 批量转换方法无需额外配置,MapStruct会自动生成
3.2 在Service中使用
@Service
public class ExcelExportService {
@Autowired
private DataConvertMapper dataConvertMapper;
private List<DataExportDto> convertToExportDto(List<Data> dataList) {
if (dataList == null || dataList.isEmpty()) {
return new ArrayList<>(0);
}
return dataConvertMapper.toExportDtoList(dataList);
}
}
3.3 查看生成的实现类
编译后,生成的实现类位于:
target/generated-sources/annotations/cn/oalo/mapper/convert/DataConvertMapperImpl.java
示例生成代码:
@Component
public class DataConvertMapperImpl implements DataConvertMapper {
@Override
public DataExportDto toExportDto(Data data) {
if (data == null) {
return null;
}
DataExportDto dto = new DataExportDto();
dto.setId(data.getId());
dto.setUsername(data.getUsername());
dto.setEmail(data.getEmail());
// ... 其他字段
return dto;
}
@Override
public List<DataExportDto> toExportDtoList(List<Data> dataList) {
if (dataList == null) {
return null;
}
List<DataExportDto> list = new ArrayList<>(dataList.size());
for (Data data : dataList) {
list.add(toExportDto(data));
}
return list;
}
}
四、高级特性
4.1 字段名不一致映射
当源对象和目标对象字段名不同时:
@Mapper(componentModel = "spring")
public interface UserMapper {
@Mapping(source = "userName", target = "name")
@Mapping(source = "userEmail", target = "email")
UserDTO toDTO(User user);
}
4.2 表达式映射(调用方法)
当需要调用对象的方法获取值时(本项目实践):
@Mapper(componentModel = "spring")
public interface DataConvertMapper {
@Mapping(target = "genderDescription", expression = "java(data.getGenderDescription())")
@Mapping(target = "statusDescription", expression = "java(data.getStatusDescription())")
DataExportDto toExportDto(Data data);
}
适用场景:
- 需要调用源对象的业务方法
- 需要进行简单的逻辑计算
- 枚举转描述文本
4.3 常量映射
@Mapper(componentModel = "spring")
public interface OrderMapper {
@Mapping(target = "type", constant = "ONLINE")
@Mapping(target = "status", constant = "PENDING")
OrderDTO toDTO(Order order);
}
4.4 忽略字段
@Mapper(componentModel = "spring")
public interface UserMapper {
@Mapping(target = "password", ignore = true)
@Mapping(target = "salt", ignore = true)
UserDTO toDTO(User user);
}
4.5 默认值设置
@Mapper(componentModel = "spring")
public interface ProductMapper {
@Mapping(target = "status", defaultValue = "ACTIVE")
ProductDTO toDTO(Product product);
}
4.6 日期格式化
@Mapper(componentModel = "spring")
public interface OrderMapper {
@Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
OrderDTO toDTO(Order order);
}
4.7 依赖注入其他转换器
@Mapper(componentModel = "spring", uses = {AddressMapper.class})
public interface UserMapper {
UserDTO toDTO(User user); // 会自动使用AddressMapper转换address字段
}
4.8 自定义转换方法
@Mapper(componentModel = "spring")
public interface UserMapper {
UserDTO toDTO(User user);
// 自定义转换逻辑
default String formatPhone(String phone) {
if (phone == null || phone.length() != 11) {
return phone;
}
return phone.substring(0, 3) + "****" + phone.substring(7);
}
}
五、与Lombok协同使用
5.1 配置要点
确保在 maven-compiler-plugin
的 annotationProcessorPaths
中按顺序配置:
- mapstruct-processor
- lombok
- lombok-mapstruct-binding
5.2 使用示例
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String id;
private String name;
private String email;
}
@Data
public class UserDTO {
private String id;
private String name;
private String email;
}
@Mapper(componentModel = "spring")
public interface UserMapper {
UserDTO toDTO(User user);
}
MapStruct会自动识别Lombok生成的getter/setter方法。
5.3 常见问题排查
如果遇到 "Unknown property" 错误:
- 检查
lombok-mapstruct-binding
是否配置 - 清理并重新编译:
mvn clean compile
- 确认处理器顺序正确
六、最佳实践
6.1 包结构规范
src/main/java/
└── cn.oalo/
├── entity/ # 实体类
│ └── Data.java
├── dto/ # 数据传输对象
│ └── DataExportDto.java
└── mapper/
├── convert/ # MapStruct转换器(推荐)
│ ├── DataConvertMapper.java
│ ├── UserConvertMapper.java
│ └── OrderConvertMapper.java
└── dm/ # MyBatis Mapper
└── DataDmMapper.java
建议:
- 将MapStruct的Mapper放在
mapper.convert
包中 - 与MyBatis的Mapper(数据访问层)区分开
6.2 命名规范
类型 | 命名规范 | 示例 |
---|---|---|
Mapper接口 | {Entity}ConvertMapper | DataConvertMapper |
转DTO方法 | toDTO / to{Type}Dto | toExportDto |
转Entity方法 | toEntity | toEntity |
批量转换 | to{Type}List | toExportDtoList |
6.3 null值处理
@Mapper(componentModel = "spring", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface UserMapper {
UserDTO toDTO(User user);
}
可选策略:
SET_TO_NULL
(默认):设置为nullIGNORE
:忽略null值,保持目标对象原值
6.4 更新现有对象
@Mapper(componentModel = "spring")
public interface UserMapper {
void updateUserFromDTO(UserDTO dto, @MappingTarget User user);
}
使用 @MappingTarget
更新现有对象,而不是创建新对象。
6.5 性能优化建议
- 批量转换:优先使用List转换方法,而非循环调用单个转换
- 避免过度映射:只映射需要的字段
- 合理使用表达式:复杂逻辑考虑使用
@AfterMapping
或自定义方法
6.6 双向映射
@Mapper(componentModel = "spring")
public interface UserMapper {
// Entity → DTO
UserDTO toDTO(User user);
// DTO → Entity
User toEntity(UserDTO dto);
// 批量转换
List<UserDTO> toDTOList(List<User> users);
List<User> toEntityList(List<UserDTO> dtos);
}
七、常见问题
7.1 编译后找不到实现类
问题表现:
No qualifying bean of type 'xxx.DataConvertMapper' available
解决方案:
- 检查
maven-compiler-plugin
配置是否正确 - 检查
mapstruct-processor
是否在annotationProcessorPaths
中 - 执行
mvn clean compile
重新编译 - 检查
@Mapper(componentModel = "spring")
是否配置
7.2 字段映射失败
问题表现:
某些字段没有被映射
排查步骤:
- 检查字段名是否完全一致(大小写敏感)
- 检查getter/setter方法是否存在
- 检查是否被
@Mapping(target = "xxx", ignore = true)
忽略 - 如果使用Lombok,确认
lombok-mapstruct-binding
已配置
7.3 Lombok兼容问题
问题表现:
Unknown property "xxx" in result type ...
解决方案:
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<!-- 必须添加这个绑定库 -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
7.4 IDEA中无法识别生成的类
解决方案:
- 右键项目 → Maven → Reload Project
- File → Invalidate Caches / Restart
- 确保
target/generated-sources/annotations
被标记为源码目录
7.5 循环依赖问题
问题表现:
A依赖B,B依赖A的Mapper
解决方案:
使用 @Context
参数传递依赖:
@Mapper(componentModel = "spring")
public interface OrderMapper {
OrderDTO toDTO(Order order, @Context CycleAvoidingMappingContext context);
}
7.6 Spring Boot DevTools热重载问题
问题表现:
使用DevTools后Mapper无法注入
解决方案:
在 application.yml
中配置:
spring:
devtools:
restart:
additional-exclude: generated-sources/**
八、版本兼容性
MapStruct版本 | Java版本 | Spring Boot版本 | Lombok版本 |
---|---|---|---|
1.5.5.Final | 8+ | 2.x / 3.x | 1.18.x |
1.5.x | 8+ | 2.x / 3.x | 1.18.x |
1.4.x | 8+ | 2.x | 1.18.x |
推荐配置:
- Java 17
- Spring Boot 3.1.4
- MapStruct 1.5.5.Final
- Lombok 1.18.30+
- lombok-mapstruct-binding 0.2.0
九、参考资源
- 官方文档: https://mapstruct.org/documentation/stable/reference/html/
GitHub仓库: https://github.com/mapstruct/mapstruct
十、快速检查清单
在新项目中集成MapStruct时,按此清单逐项检查:
- [ ] 1. 在pom.xml的
<properties>
中添加版本号 - [ ] 2. 在
<dependencies>
中添加mapstruct依赖 - [ ] 3. 配置maven-compiler-plugin
- [ ] 4. 在
annotationProcessorPaths
中添加mapstruct-processor - [ ] 5. 如果使用Lombok,添加lombok和lombok-mapstruct-binding
- [ ] 6. 创建mapper.convert包
- [ ] 7. 创建Mapper接口并添加
@Mapper(componentModel = "spring")
- [ ] 8. 定义转换方法
- [ ] 9. 执行
mvn clean compile
- [ ] 10. 在Service中注入Mapper并使用
- [ ] 11. 检查target/generated-sources/annotations中的实现类
- [ ] 12. 运行测试验证功能
站点网址:https://www.jiafeng.fun
站点头像:https://www.jiafeng.fun/favicon.ico
站点简介(可无):个人博客,前端技术分享