MapStruct使用说明

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-pluginannotationProcessorPaths 中按顺序配置:

  1. mapstruct-processor
  2. lombok
  3. 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" 错误:

  1. 检查 lombok-mapstruct-binding 是否配置
  2. 清理并重新编译:mvn clean compile
  3. 确认处理器顺序正确

六、最佳实践

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}ConvertMapperDataConvertMapper
转DTO方法toDTO / to{Type}DtotoExportDto
转Entity方法toEntitytoEntity
批量转换to{Type}ListtoExportDtoList

6.3 null值处理

@Mapper(componentModel = "spring", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface UserMapper {
    UserDTO toDTO(User user);
}

可选策略:

  • SET_TO_NULL(默认):设置为null
  • IGNORE:忽略null值,保持目标对象原值

6.4 更新现有对象

@Mapper(componentModel = "spring")
public interface UserMapper {
    
    void updateUserFromDTO(UserDTO dto, @MappingTarget User user);
}

使用 @MappingTarget 更新现有对象,而不是创建新对象。

6.5 性能优化建议

  1. 批量转换:优先使用List转换方法,而非循环调用单个转换
  2. 避免过度映射:只映射需要的字段
  3. 合理使用表达式:复杂逻辑考虑使用 @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

解决方案:

  1. 检查 maven-compiler-plugin 配置是否正确
  2. 检查 mapstruct-processor 是否在 annotationProcessorPaths
  3. 执行 mvn clean compile 重新编译
  4. 检查 @Mapper(componentModel = "spring") 是否配置

7.2 字段映射失败

问题表现:
某些字段没有被映射

排查步骤:

  1. 检查字段名是否完全一致(大小写敏感)
  2. 检查getter/setter方法是否存在
  3. 检查是否被 @Mapping(target = "xxx", ignore = true) 忽略
  4. 如果使用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中无法识别生成的类

解决方案:

  1. 右键项目 → Maven → Reload Project
  2. File → Invalidate Caches / Restart
  3. 确保 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.Final8+2.x / 3.x1.18.x
1.5.x8+2.x / 3.x1.18.x
1.4.x8+2.x1.18.x

推荐配置:

  • Java 17
  • Spring Boot 3.1.4
  • MapStruct 1.5.5.Final
  • Lombok 1.18.30+
  • lombok-mapstruct-binding 0.2.0

九、参考资源

十、快速检查清单

在新项目中集成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. 运行测试验证功能
打赏
评论区
头像
文章目录

本网站由提供CDN加速/云存储服务