一、引入依赖
以 Maven 为例:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
<scope>provided</scope>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
————————————————
版权声明:本文为CSDN博主「猩火燎猿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/onlymscn/article/details/153517590
二、基本用法
1. 定义源对象和目标对象
public class UserEntity {
private Long id;
private String name;
private Integer age;
// getter/setter
}
public class UserDTO {
private Long id;
private String name;
private Integer age;
// getter/setter
}
2. 定义 Mapper 接口
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserDTO entityToDto(UserEntity entity);
UserEntity dtoToEntity(UserDTO dto);
}
3. 使用 Mapper
UserEntity entity = new UserEntity(1L, "Tom", 20);
UserDTO dto = UserMapper.INSTANCE.entityToDto(entity);
三、常用配置详解
1. 属性名不一致时
public class UserEntity {
private String userName;
// ...
}
public class UserDTO {
private String name;
// ...
}
@Mapper
public interface UserMapper {
@Mapping(source = "userName", target = "name")
UserDTO entityToDto(UserEntity entity);
}
2. 忽略某些字段
@Mapper
public interface UserMapper {
@Mapping(target = "age", ignore = true)
UserDTO entityToDto(UserEntity entity);
}
3. 字段类型转换
public class UserEntity {
private String age; // String 类型
}
public class UserDTO {
private Integer age; // Integer 类型
}
@Mapper
public interface UserMapper {
@Mapping(source = "age", target = "age")
UserDTO entityToDto(UserEntity entity);
}
MapStruct 支持常见类型自动转换,也可以自定义转换方法。
4. 多个字段合并/拆分
public class UserEntity {
private String firstName;
private String lastName;
}
public class UserDTO {
private String fullName;
}
@Mapper
public interface UserMapper {
@Mapping(target = "fullName", expression = "java(entity.getFirstName() + \" \" + entity.getLastName())")
UserDTO entityToDto(UserEntity entity);
}
5. List、Set、Map 的映射
List<UserDTO> dtoList = UserMapper.INSTANCE.entityListToDtoList(entityList);
@Mapper
public interface UserMapper {
List<UserDTO> entityListToDtoList(List<UserEntity> entityList);
}
6. 使用 @MapperConfig 做全局配置
@MapperConfig(
componentModel = "spring",
unmappedTargetPolicy = ReportingPolicy.IGNORE
)
public interface GlobalConfig {
}
然后在 Mapper 中引用:
@Mapper(config = GlobalConfig.class)
public interface UserMapper { ... }
7. 支持 Spring、Jsr330 等依赖注入
@Mapper(componentModel = "spring")
public interface UserMapper { ... }
这样可以通过 Spring 注入 Mapper。
8. 其他常用注解
@Mappings:多个 @Mapping 的集合(新版本可直接多个 @Mapping)@InheritInverseConfiguration:反向映射@AfterMapping、@BeforeMapping:映射前后处理
四、常见问题
- 编译未生成实现类?
- 检查是否引入了
mapstruct-processor依赖,IDE 插件是否支持注解处理。
- 检查是否引入了
- Lombok 冲突?
- 引入
lombok-mapstruct-binding。
- 引入
- 复杂嵌套映射?
- 支持嵌套对象映射,定义多个 Mapper 并用
uses属性引用。
- 支持嵌套对象映射,定义多个 Mapper 并用
五、进阶用法
1. 自定义方法和表达式
除了基本的字段映射,你可以在 Mapper 中定义自定义方法用于复杂转换:
@Mapper
public interface UserMapper {
@Mapping(target = "fullName", expression = "java(concatName(entity.getFirstName(), entity.getLastName()))")
UserDTO entityToDto(UserEntity entity);
default String concatName(String firstName, String lastName) {
return firstName + " " + lastName;
}
}
2. 嵌套对象映射
如果 DTO 中包含嵌套对象,可以通过 uses 属性指定其他 Mapper:
public class AddressEntity { ... }
public class AddressDTO { ... }
@Mapper
public interface AddressMapper {
AddressDTO entityToDto(AddressEntity entity);
}
public class UserEntity {
private AddressEntity address;
// ...
}
public class UserDTO {
private AddressDTO address;
// ...
}
@Mapper(uses = AddressMapper.class)
public interface UserMapper {
UserDTO entityToDto(UserEntity entity);
}
3. @InheritConfiguration 和 @InheritInverseConfiguration
用于复用映射规则,减少重复代码:
@Mapper
public interface UserMapper {
@Mapping(source = "userName", target = "name")
UserDTO entityToDto(UserEntity entity);
@InheritInverseConfiguration
UserEntity dtoToEntity(UserDTO dto);
}
4. 默认值和常量
可以为目标字段设置默认值或常量:
@Mapping(target = "status", constant = "ACTIVE")
@Mapping(target = "age", defaultValue = "18")
UserDTO entityToDto(UserEntity entity);
5. 枚举类型映射
MapStruct 支持自动枚举类型映射,也可自定义:
public enum UserStatusEntity { ENABLED, DISABLED }
public enum UserStatusDTO { ACTIVE, INACTIVE }
@Mapper
public interface UserMapper {
@Mapping(source = "status", target = "status")
UserDTO entityToDto(UserEntity entity);
default UserStatusDTO mapStatus(UserStatusEntity status) {
if (status == UserStatusEntity.ENABLED) return UserStatusDTO.ACTIVE;
else return UserStatusDTO.INACTIVE;
}
}
6. @AfterMapping 和 @BeforeMapping
在映射前后做处理:
@Mapper
public interface UserMapper {
UserDTO entityToDto(UserEntity entity);
@AfterMapping
default void afterMapping(@MappingTarget UserDTO dto, UserEntity entity) {
dto.setName(dto.getName().toUpperCase());
}
}
六、常用配置参数说明
| 参数名 | 说明 |
|---|---|
| componentModel | Mapper 实现类的注入方式(default、spring、jsr330、cdi) |
| uses | 引用其他 Mapper |
| unmappedTargetPolicy | 未映射字段的处理策略(IGNORE, WARN, ERROR) |
| mappingInheritanceStrategy | 映射继承策略(AUTO, EXPLICIT, NONE) |
示例:
@Mapper(
componentModel = "spring",
uses = {AddressMapper.class},
unmappedTargetPolicy = ReportingPolicy.WARN
)
public interface UserMapper { ... }
七、MapStruct 与 Spring 集成
如果你用 Spring Boot,可以这样:
@Mapper(componentModel = "spring")
public interface UserMapper { ... }
然后直接注入:
@Autowired
private UserMapper userMapper;
八、批量映射
MapStruct 支持集合类型的批量映射:
List<UserDTO> entityListToDtoList(List<UserEntity> entityList);
Set<UserDTO> entitySetToDtoSet(Set<UserEntity> entitySet);
九、常见实战场景
1. DTO 与 Entity 双向转换
定义双向方法,并用 @InheritInverseConfiguration 简化:
@Mapper
public interface UserMapper {
@Mapping(source = "userName", target = "name")
UserDTO entityToDto(UserEntity entity);
@InheritInverseConfiguration
UserEntity dtoToEntity(UserDTO dto);
}
2. 分页对象转换
@Mapper
public interface UserMapper {
Page<UserDTO> entityPageToDtoPage(Page<UserEntity> entityPage);
}
(前提是你的分页对象有合适的 getter/setter)
3. 多层嵌套复杂对象转换
通过 uses 属性引用多个 Mapper,逐层转换。
十、MapStruct 性能与优点
- 编译期生成代码,性能极高。
- 类型安全,编译报错,减少运行期异常。
- 易于维护,减少手写转换代码。
十一、常见问题排查
- 实现类未生成?
- 检查 IDE 是否开启注解处理器(annotation processor)。
- Lombok 与 MapStruct 冲突?
- 引入
lombok-mapstruct-binding,并确保 getter/setter 正确。
- 引入
- 复杂类型映射失败?
- 检查
uses配置,确认所有 Mapper 都已声明。
- 检查
十二、MapStruct 高级用法
1. 多源对象合并映射(多个参数)
有时目标对象需要来自多个源对象的数据。例如:
public class UserEntity { private String name; }
public class AddressEntity { private String city; }
public class UserDTO { private String name; private String city; }
@Mapper
public interface UserMapper {
@Mapping(source = "user.name", target = "name")
@Mapping(source = "address.city", target = "city")
UserDTO toDto(UserEntity user, AddressEntity address);
}
2. 映射方法重载和条件映射
可以根据不同参数类型定义多个方法:
UserDTO toDto(UserEntity entity);
UserDTO toDto(UserEntity entity, String extraInfo);
@Mapping(target = "extraInfo", expression = "java(extraInfo)")
UserDTO toDto(UserEntity entity, String extraInfo);
3. 自定义类型转换器(Type Conversion)
对复杂类型或特殊格式进行转换:
@Mapper
public interface UserMapper {
@Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")
UserDTO entityToDto(UserEntity entity);
}
或者使用自定义方法:
default String mapDate(LocalDate date) {
return date != null ? date.format(DateTimeFormatter.ISO_DATE) : null;
}
4. 分组映射(@MappingTarget)
用于更新已存在的对象(例如数据库更新):
@Mapper
public interface UserMapper {
void updateUserFromDto(UserDTO dto, @MappingTarget UserEntity entity);
}
这样可以只更新部分字段,而不是创建新对象。
十三、调试与代码生成检查
1. 查看 MapStruct 生成的实现类
MapStruct 在编译时生成实现类,一般在 target/generated-sources/annotations 目录下。
你可以直接查看生成的代码,了解转换细节,定位问题。
2. IDE 设置注解处理器
- IntelliJ IDEA:
Settings -> Build, Execution, Deployment -> Compiler -> Annotation Processors,勾选“Enable annotation processing”。 - Eclipse:
Project -> Properties -> Java Compiler -> Annotation Processing,开启注解处理。
3. 日志与错误提示
MapStruct 的错误和警告会在编译期给出,建议 unmappedTargetPolicy 设置为 WARN 或 ERROR,及时发现未映射字段。
十四、与常用技术框架集成
1. 与 Spring Boot 集成
如前所述,设置 @Mapper(componentModel = "spring"),然后用 @Autowired 注入即可。
2. 与 MyBatis、JPA/Hibernate 集成
MapStruct 主要用于 DTO 和 Entity 的转换,和 MyBatis Mapper 或 JPA Repository 配合非常方便。
你可以在 Service 层用 MapStruct 进行数据转换,然后调用持久层操作。
3. 与 Lombok 集成
- 推荐引入
lombok-mapstruct-binding。 - 保证实体类
@Data、@Getter、@Setter注解齐全。 - 避免用 Lombok 的
@Builder造成构造器冲突。
十五、最佳实践与建议
- 保持 Mapper 接口简洁,只做映射,不做业务逻辑。
- 复杂转换用自定义方法或表达式,避免在 Mapper 里写复杂代码。
- 充分利用全局配置(@MapperConfig),统一 unmappedTargetPolicy、componentModel 等参数。
- 嵌套对象用
uses引用其他 Mapper,保持结构清晰。 - 及时查看和维护 MapStruct 生成的代码,发现潜在问题。
十六、常见业务场景示例
1. 表单对象(Form)与 DTO/Entity 转换
@Mapper(componentModel = "spring")
public interface UserFormMapper {
UserDTO formToDto(UserForm form);
UserForm dtoToForm(UserDTO dto);
}
2. 响应对象(VO)与 DTO 转换
@Mapper(componentModel = "spring")
public interface UserVoMapper {
UserVO dtoToVo(UserDTO dto);
}
3. 批量数据处理
List<UserDTO> entitiesToDtos(List<UserEntity> entities);
4. 分页结果转换
假设你用的是 MyBatis Plus 或 Spring Data 的分页对象:
@Mapper(componentModel = "spring")
public interface PageMapper {
default <T, U> Page<U> pageToPage(Page<T> page, Function<T, U> converter) {
List<U> records = page.getRecords().stream().map(converter).collect(Collectors.toList());
return new PageImpl<>(records, page.getPageable(), page.getTotalElements());
}
}


被折叠的 条评论
为什么被折叠?



