详解MapStruct Plus

一、MapStruct Plus 简介

MapStruct Plus 是基于 MapStruct 框架的增强工具,旨在通过注解驱动的方式,自动生成 Java 类型之间的映射接口,省略手动定义映射接口的步骤,使 Java 类型转换更加便捷、优雅。它继承了 MapStruct 的高性能特点,同时增强了便携性和快速开发的特性,尤其适合处理多层对象模型、复杂嵌套结构以及快速原型开发场景。

二、核心特性
  1. 自动化映射接口生成:通过 @AutoMapper 注解自动生成两个类之间的映射接口,无需手动编写接口方法。
  2. 复杂对象深度转换支持:自动处理嵌套对象映射,无需手动定义每个层级的转换逻辑。
  3. Map 与对象互转增强:支持 Map 转换为自定义类,甚至嵌套 Map 结构。
  4. 多目标对象配置:通过 @AutoMappers 注解支持单个源对象映射到多个目标对象。
  5. 兼容性与扩展性:完全兼容 MapStruct,可直接替换依赖;支持 JDK 8~17 和 Spring Boot 2~3;提供 SPI 扩展点,允许自定义类型转换逻辑。
三、快速入门
1. 引入依赖

在 Maven 项目中,添加以下依赖:

<dependencies>
    <!-- MapStruct Plus 核心依赖 -->
    <dependency>
        <groupId>io.github.linpeilie</groupId>
        <artifactId>mapstruct-plus</artifactId>
        <version>最新版本</version>
    </dependency>
    <!-- MapStruct Plus 处理器(编译时生成代码) -->
    <dependency>
        <groupId>io.github.linpeilie</groupId>
        <artifactId>mapstruct-plus-processor</artifactId>
        <version>最新版本</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
2. 基本使用

场景 1:简单对象转换

假设有两个类 UserDto 和 User,分别表示数据层对象和业务层对象:

// UserDto.java
public class UserDto {
    private String username;
    private int age;
    private boolean young;
    // getter、setter、toString、equals、hashCode
}

// User.java
public class User {
    private String username;
    private int age;
    private boolean young;
    // getter、setter、toString、equals、hashCode
}

使用 MapStruct Plus 进行转换

  • 方式 1:在其中一个类上添加 @AutoMapper 注解
@AutoMapper(target = UserDto.class)
public class User {
    // 字段定义
}
  • 方式 2:定义映射接口(可选,若需自定义转换逻辑)
@AutoMapper
public interface UserMapper {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
    UserDto toDto(User user);
    User toEntity(UserDto dto);
}

测试转换

public class QuickStartTest {
    private static Converter converter = new Converter();

    @Test
    public void testConversion() {
        User user = new User();
        user.setUsername("jack");
        user.setAge(23);
        user.setYoung(false);

        // User -> UserDto
        UserDto userDto = converter.convert(user, UserDto.class);
        System.out.println(userDto); // UserDto{username='jack', age=23, young=false}
        assert user.getUsername().equals(userDto.getUsername());
        assert user.getAge() == userDto.getAge();
        assert user.isYoung() == userDto.isYoung();

        // UserDto -> User
        User newUser = converter.convert(userDto, User.class);
        System.out.println(newUser); // User{username='jack', age=23, young=false}
        assert user.getUsername().equals(newUser.getUsername());
        assert user.getAge() == newUser.getAge();
        assert user.isYoung() == newUser.isYoung();
    }
}
3. 高级特性

场景 2:复杂对象转换(嵌套结构)

// OrderDto.java
public class OrderDto {
    private Long id;
    private UserDto user;
    // getter、setter
}

// Order.java
public class Order {
    private Long id;
    private User user;
    // getter、setter
}

// 在 Order 或 User 类上添加 @AutoMapper 注解
@AutoMapper(target = OrderDto.class)
public class Order {
    // 字段定义
}

测试嵌套转换

@Test
public void testNestedConversion() {
    Order order = new Order();
    order.setId(1L);
    User user = new User();
    user.setUsername("jack");
    order.setUser(user);

    OrderDto orderDto = converter.convert(order, OrderDto.class);
    System.out.println(orderDto); // OrderDto{id=1, user=UserDto{username='jack', age=0, young=false}}
    assert order.getId().equals(orderDto.getId());
    assert order.getUser().getUsername().equals(orderDto.getUser().getUsername());
}

场景 3:枚举和特殊规则处理

// Status.java
public enum Status {
    ACTIVE, INACTIVE
}

// ProjectDto.java
public class ProjectDto {
    private String name;
    private Status status;
    // getter、setter
}

// Project.java
public class Project {
    private String name;
    private String status; // 存储为字符串
    // getter、setter
}

// 自定义转换逻辑
@AutoMapper
public interface ProjectMapper {
    ProjectMapper INSTANCE = Mappers.getMapper(ProjectMapper.class);

    @Mapping(target = "status", expression = "java(dto.getStatus().name())")
    Project toEntity(ProjectDto dto);

    @Mapping(target = "status", expression = "java(Status.valueOf(entity.getStatus()))")
    ProjectDto toDto(Project entity);
}

测试枚举转换

@Test
public void testEnumConversion() {
    ProjectDto dto = new ProjectDto();
    dto.setName("Project A");
    dto.setStatus(Status.ACTIVE);

    Project project = ProjectMapper.INSTANCE.toEntity(dto);
    System.out.println(project); // Project{name='Project A', status='ACTIVE'}
    assert dto.getName().equals(project.getName());
    assert dto.getStatus().name().equals(project.getStatus());

    ProjectDto newDto = ProjectMapper.INSTANCE.toDto(project);
    System.out.println(newDto); // ProjectDto{name='Project A', status=ACTIVE}
    assert project.getName().equals(newDto.getName());
    assert project.getStatus().equals(newDto.getStatus().name());
}

场景 4:列表转换

@Test
public void testListConversion() {
    List<User> users = new ArrayList<>();
    users.add(new User("jack", 23, false));
    users.add(new User("alice", 25, true));

    List<UserDto> userDtos = converter.convertList(users, UserDto.class);
    System.out.println(userDtos); // [UserDto{username='jack', age=23, young=false}, UserDto{username='alice', age=25, young=true}]
    assert users.size() == userDtos.size();
    assert users.get(0).getUsername().equals(userDtos.get(0).getUsername());
}
四、与 Spring Boot 集成

MapStruct Plus 可以与 Spring Boot 无缝集成,支持依赖注入和自动配置。

1. 添加 Spring Boot Starter依赖
<dependency>
    <groupId>io.github.linpeilie</groupId>
    <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
    <version>1.5.0</version> <!-- 使用最新版本 -->
</dependency>
2. 配置组件模型

在 @AutoMapper 或 @Mapper 注解中指定 componentModel = "spring"

@AutoMapper(target = UserDto.class, componentModel = "spring")
public class User {
    // 字段定义
}

@Mapper(componentModel = "spring")
public interface UserMapper {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
    UserDto toDto(User user);
    User toEntity(UserDto dto);
}
3. 注入并使用 Converter
@SpringBootTest
public class SpringBootQuickStartTest {
    @Autowired
    private Converter converter;

    @Test
    public void testConversion() {
        User user = new User();
        user.setUsername("jack");
        user.setAge(23);
        user.setYoung(false);

        UserDto userDto = converter.convert(user, UserDto.class);
        System.out.println(userDto); // UserDto{username='jack', age=23, young=false}
    }
}
五、最佳实践
  • 合理使用 @AutoMapper 和 @Mapper
    • 若需简单快速转换,且对控制粒度要求不高,使用 @AutoMapper
    • 若需自定义转换逻辑或处理复杂场景,使用 @Mapper 定义接口。
  • 处理字段名不一致
    • 使用 @Mapping 注解指定字段映射关系:
@Mapping(source = "userName", target = "username")
UserDto toDto(User user);
  • 忽略字段
    • 使用 @Mapping(target = "fieldName", ignore = true) 忽略不需要映射的字段。
  • 处理集合类型
    • MapStruct Plus 自动处理集合到集合的映射,无需特别配置。
  • 性能优化
    • MapStruct Plus 生成的代码基于 getter/setter 方法,性能接近手动编写代码。
    • 避免在映射逻辑中执行复杂计算或数据库操作。
  • 调试与错误处理
    • 编译时生成的代码位于 target/generated-sources/annotations 目录下,可查看生成的实现类。
    • 若映射错误,编译时会报错,根据错误信息调整映射逻辑。
六、常见问题解答
  • Q:MapStruct Plus 与 MapStruct 有什么区别?
    • A:MapStruct Plus 是 MapStruct 的增强工具,通过 @AutoMapper 注解自动生成映射接口,省略手动定义接口的步骤,使开发更加便捷。
  • Q:MapStruct Plus 支持哪些 Java 版本?
    • A:支持 JDK 8~17。
  • Q:MapStruct Plus 是否支持 Lombok?
    • A:支持,但需确保 Lombok 注解处理器在编译时生效。
  • Q:如何处理循环引用?
    • A:MapStruct Plus 默认不支持循环引用,需手动处理或使用 @Context 注解传递上下文。
  • Q:如何自定义类型转换逻辑?
    • A:通过 @Mapper 定义接口,并在接口方法中实现自定义逻辑;或使用 expression 属性指定 Java 表达式。
七、总结

MapStruct Plus 是一个强大的 Java 类型转换工具,通过注解驱动的方式自动生成映射接口,显著简化了复杂对象模型的转换流程。它继承了 MapStruct 的高性能特点,同时增强了便携性和快速开发的特性,尤其适合处理多层对象模型、复杂嵌套结构以及快速原型开发场景。

通过合理使用 @AutoMapper 和 @Mapper 注解,结合高级特性如枚举转换、列表转换等,可以高效地完成各种 Java 类型转换任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jack_abu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值