侧边栏壁纸
博主头像
Ivan Zhang

所谓更牛,就是换个罪受

  • 累计撰写 49 篇文章
  • 累计创建 54 个标签
  • 累计收到 6 条评论

目 录CONTENT

文章目录

MapStruct 实用指南

Ivan Zhang
2025-09-25 / 0 评论 / 0 点赞 / 27 阅读 / 6,972 字
温馨提示:
本文最后更新于 ,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。
有什么问题或观点欢迎评论留言,或者 交流。
如果觉得文章对您有所帮助,可以给博主打赏鼓励一下。

一、概述

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 提供了强大而高效的对象映射解决方案。通过正确的配置和最佳实践,可以:

  1. ✅ 实现零运行时开销的对象转换
  2. ✅ 保证类型安全和编译时检查
  3. ✅ 与 Spring 和 Lombok 完美集成
  4. ✅ 处理复杂的嵌套映射场景
  5. ✅ 避免常见的映射陷阱和错误

建议在新项目中直接采用本指南的配置和实践,可以显著提高开发效率和代码质量。

0

评论区