一、概述
MapStruct 是一个基于注解的 Java 对象映射框架,通过在编译时生成映射实现代码,提供高性能的类型安全对象转换。
核心优势
- 零运行时开销:编译时生成代码,无反射调用
- 类型安全:编译时检查映射错误
- 易于调试:生成的可读性高的实现代码
- 与 Lombok 完美集成:支持自动 Getter/Setter 生成
二、项目配置
Maven 配置(JDK 8)
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<mapstruct.version>1.5.5.Final</mapstruct.version>
<mapstruct-processor.version>1.5.5.Final</mapstruct-processor.version>
<lombok.version>1.18.30</lombok.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
<dependencies>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- MapStruct 核心依赖 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<!-- MapStruct 处理器依赖 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct-processor.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<annotationProcessorPaths>
<!-- 处理顺序很重要:Lombok → Binding → MapStruct -->
<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>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct-processor.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
Lombok 配置(非必须)
在项目根目录创建 lombok.config
:
lombok.accessors.chain=true
lombok.getter.noIsPrefix=true
lombok.toString.includeFieldNames=true
lombok.anyConstructor.addConstructorProperties=true
三、基础用法
1. 简单映射接口
@Mapper(componentModel = "spring")
public interface UserMapper {
@Mapping(source = "username", target = "name")
@Mapping(source = "createTime", target = "createDate", dateFormat = "yyyy-MM-dd")
UserDTO toDTO(UserEntity entity);
UserEntity toEntity(UserDTO dto);
}
2. 集合映射
@Mapper(componentModel = "spring")
public interface UserMapper {
List<UserDTO> toDTOList(List<UserEntity> entities);
Set<UserDTO> toDTOSet(Set<UserEntity> entities);
}
四、高级特性
1. 嵌套对象映射
public class OrderEntity {
private Long id;
private UserEntity user;
private List<OrderItemEntity> items;
}
public class OrderDTO {
private Long id;
private UserDTO user;
private List<OrderItemDTO> items;
}
@Mapper(componentModel = "spring")
public interface OrderMapper {
// Mapping 注解非必须
@Mapping(source = "user", target = "user")
@Mapping(source = "items", target = "items")
OrderDTO toDTO(OrderEntity entity);
// 需要提供嵌套对象的映射方法
UserDTO toDTO(UserEntity entity);
List<OrderItemDTO> toDTOList(List<OrderItemEntity> entities);
}
2. 多源参数映射
@Mapper(componentModel = "spring")
public interface UserMapper {
@Mappings({
@Mapping(source = "user.name", target = "username"),
@Mapping(source = "address.city", target = "city")
})
UserDTO merge(UserEntity user, AddressEntity address);
}
3. 自定义映射方法
@Mapper(componentModel = "spring")
public abstract class UserMapper {
public UserDTO toDTO(UserEntity entity) {
if (entity == null) {
return null;
}
UserDTO dto = new UserDTO();
dto.setName(entity.getFirstName() + " " + entity.getLastName());
dto.setAge(calculateAge(entity.getBirthDate()));
return dto;
}
private Integer calculateAge(LocalDate birthDate) {
return Period.between(birthDate, LocalDate.now()).getYears();
}
}
五、解决常见问题
1. 方法签名冲突(类型擦除)
// 错误:方法签名冲突
List<PageModuleField> map(List<PageModuleField> source);
List<PageFieldCalculationRule> map(List<PageFieldCalculationRule> source);
// 正确:使用不同方法名
List<PageModuleField> mapFieldList(List<PageModuleField> source);
List<PageFieldCalculationRule> mapRuleList(List<PageFieldCalculationRule> source);
2. Lombok 集成问题
确保类有正确的 Getter 方法(如上 xml 配置添加 plugin 插件,确保 Getter 方法在 mapstruct 调用前生成):
// 推荐使用独立类而非接口内部类
@Data
@Accessors(chain = true)
public class Page {
private Base base;
private List<PageModule> pageModuleList;
}
3. 枚举映射
public enum UserType {
ADMIN, USER, GUEST
}
public enum UserTypeDTO {
ADMINISTRATOR, STANDARD_USER, VISITOR
}
@Mapper(componentModel = "spring")
public interface UserMapper {
@ValueMapping(source = "ADMIN", target = "ADMINISTRATOR")
@ValueMapping(source = "USER", target = "STANDARD_USER")
@ValueMapping(source = "GUEST", target = "VISITOR")
UserTypeDTO toDTO(UserType type);
}
六、最佳实践
1. 项目结构
src/
├── main/
│ ├── java/
│ │ ├── com/example/dto/
│ │ ├── com/example/entity/
│ │ └── com/example/mapstruct/ # 所有映射器接口
│ └── resources/
└── test/
2. 命名规范
- 映射器接口:
XxxMapper
,如果要和 Mybatis 等区分,可以使用XxxConverter
- 方法名:
toDTO
,toEntity
,toVO
等 - 列表映射:
toDTOList
,toEntityList
3. 配置建议
@Mapper(componentModel = "spring",
unmappedTargetPolicy = ReportingPolicy.IGNORE,
unmappedSourcePolicy = ReportingPolicy.IGNORE,
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface BaseMapper {
// 基础配置
}
4. 测试验证
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testToDTO() {
UserEntity entity = new UserEntity()
.setUsername("test")
.setEmail("test@example.com");
UserDTO dto = userMapper.toDTO(entity);
assertNotNull(dto);
assertEquals("test", dto.getName());
}
}
七、常见问题排查
1. 编译后无生成代码
- 检查注解处理器配置顺序
- 验证 Lombok 是否正常工作:
mvn lombok:delombok
- 查看
target/generated-sources/annotations/
目录
2. 属性无法映射
- 确保源类和目标类都有正确的 Getter/Setter
- 检查字段名称是否匹配
- 使用
@Mapping
注解明确指定映射关系
3. 循环依赖
// 使用 ignore 避免循环映射
@Mapping(target = "parent", ignore = true)
CategoryDTO toDTO(CategoryEntity entity);
八、性能优化
1. 重用映射实例
@Mapper(componentModel = "spring")
public interface UserMapper {
// 映射实例会被缓存和重用
UserDTO toDTO(UserEntity entity);
}
2. 避免不必要的映射
@Mapper(componentModel = "spring")
public interface UserMapper {
// 忽略不需要的字段
@Mapping(target = "password", ignore = true)
@Mapping(target = "sensitiveData", ignore = true)
UserDTO toSafeDTO(UserEntity entity);
}
九、版本兼容性参考
Lombok 版本 | MapStruct 版本 | lombok-mapstruct-binding 版本 |
---|---|---|
1.18.16+ | 1.4.0+ | 0.2.0 |
1.18.12+ | 1.3.1+ | 0.1.0 |
1.18.8+ | 1.3.0+ | 0.0.6 |
十、总结
MapStruct 提供了强大而高效的对象映射解决方案。通过正确的配置和最佳实践,可以:
- ✅ 实现零运行时开销的对象转换
- ✅ 保证类型安全和编译时检查
- ✅ 与 Spring 和 Lombok 完美集成
- ✅ 处理复杂的嵌套映射场景
- ✅ 避免常见的映射陷阱和错误
建议在新项目中直接采用本指南的配置和实践,可以显著提高开发效率和代码质量。
评论区