找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1014

积分

0

好友

130

主题
发表于 昨天 20:04 | 查看: 0| 回复: 0

在后端业务开发中,处理对象之间的转换是家常便饭。尽管MapStruct通过编译时生成代码的方式帮助我们提升了效率,但手动编写 @Mapper 接口和映射方法的步骤依然让开发者感到繁琐。有没有办法更进一步简化?MapStruct 的增强版—— MapStruct Plus 应运而生。它继承了原生 MapStruct 编译期生成、高性能的优势,并带来了诸如“自动生成 Mapper 接口”等革命性特性,让对象转换变得前所未有的简单。

🚀 MapStructPlus 核心优势:对比原生MapStruct

MapStruct Plus 基于原生 MapStruct 进行增强,遵循“只做增强,不做修改”的原则,旨在让对象映射更简单、更优雅:

  • 零接口定义:无需手动编写 @Mapper 注解的接口。只需在实体类上添加一行 @AutoMapper 注解来声明映射关系,即可省去大量转换代码。
  • 自动生成实现:在编译期自动生成高效的原生 Java 转换代码,其性能与手写代码几乎无差别。
  • 多环境适配:完美支持 SpringBoot 环境,也兼容非 SpringBoot 项目,依赖配置极其简洁。
  • 功能强化:针对原生 MapStruct 中需要复杂配置的场景(如嵌套对象映射、枚举映射、一对多转换),MapStruct Plus 仅通过注解即可轻松实现。

MapStructPlus官网首页截图,标题为“可能是最简单最强大的Java Bean转换工具”

📌 快速入门

1️⃣ SpringBoot环境引入依赖

在 SpringBoot 项目中,引入以下依赖即可快速开始。starter 会自动引入必要的处理器。

<dependencies>
    <!-- 核心 starter,自动引入处理器和依赖 -->
    <dependency>
        <groupId>io.github.linpeilie</groupId>
        <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
        <version>1.4.8</version>
    </dependency>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-core</artifactId>
      <version>你的 hutool 版本</version>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>你的 Lombok 版本</version>
                    </path>
                    <path>
                      <!-- Lombok 与 MapStruct 绑定器(Lombok 1.18.16+ 版本需加上) -->
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok-mapstruct-binding</artifactId>
                        <version>0.2.0</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

2️⃣ 配置与使用映射

核心流程非常简单:使用注解标记映射关系,然后通过自动注入的 Converter 工具类完成转换。

// 1. 在源类添加@AutoMapper注解,指定目标类
@Data
@AutoMapper(target = UserDto.class)
public class User {
    // 字段同上
    private String username;
    private int age;
    private boolean young;
    //指定时间格式进行转换
    @AutoMapping(dateFormat = "yyyy-MM-dd HH:mm:ss")
    private Date birthday;
}

@Data
public class UserDto {
    private String username;
    private int age;
    private boolean young;
    private String birthday; // 时间字段类型不一致(Date → String)
}

// 2. SpringBoot 环境测试(自动注入 Converter)
@SpringBootTest
public class QuickStartTest {
    @Autowired
    private Converter converter;

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

        // 实体转 DTO
        UserDto userDto = converter.convert(user, UserDto.class);
        System.out.println(userDto);
        // 输出:UserDto(username=jack, age=23, young=false, birthday=2026-01-28 11:00:00)

        // DTO 转实体(支持自动反向转换)
        User newUser = converter.convert(userDto, User.class);
        assert user.getUsername().equals(newUser.getUsername());
    }
}

🔧 功能详解

一、字段映射自定义

当源类与目标类的字段名、类型不一致时,可以使用 @AutoMapping 注解进行精细化配置,支持格式转换、忽略字段等操作。

1. 字段名不一致映射

@Data
@AutoMapper(target = UserDto.class)
public class User {
    private String username; // 源字段
    @AutoMapping(target = “userAge”) // 目标字段名:userAge
    private int age;
    @AutoMapping(defaultValue = “false”)// 为 null 时,转换到目标类后的默认值
    private boolean young;
}

2. 类型转换与格式指定

支持对日期、数字等类型进行自定义格式转换,无需手动编写转换逻辑。

@Data
@AutoMapper(target = UserDto.class)
public class User {
    private String username;
    // 数字格式:保留两位小数,前缀加 $
    @AutoMapping(target = “assetsStr”, numberFormat = “$0.00”)
    private double assets;
    // 日期格式:自定义字符串格式
    @AutoMapping(target = “birthdayStr”, dateFormat = “yyyy-MM-dd”)
    private Date birthday;
}

3. 忽略指定字段

对于不需要参与映射的字段,可以直接标记 ignore = true

@Data
@AutoMapper(target = UserDto.class)
public class User {
    private String username;
    private int age;
    // 忽略密码字段,不参与映射
    @AutoMapping(target = “password”, ignore = true)
    private String password;
}

二、多类映射(一个类对应多个目标类)

当一个实体需要转换为多个不同的 DTO 或 VO 时,可以使用 @AutoMappers 注解进行批量配置,并支持为不同目标类指定差异化的映射规则。

@Data
@AutoMappers({
    @AutoMapper(target = UserDto.class), // 对应 UserDto
    @AutoMapper(target = UserVo.class)   // 对应 UserVo
})
public class User {
    private String username;
    private int age;
    private Date birthday;

    // 对 UserDto 单独配置:生日转字符串格式
    @AutoMapping(targetClass = UserDto.class, target = “birthday”, dateFormat = “yyyy-MM-dd”)
    // 对 UserVo 单独配置:忽略生日字段
    @AutoMapping(targetClass = UserVo.class, target = “birthday”, ignore = true)
    private Date birthday;

    // 未指定 targetClass,对所有目标类生效
    @AutoMapping(target = “userName”, source = “username”)
    private String username;
}

提示:targetClass 支持父类,其子类会自动继承映射规则(1.3.6 版本及以上支持)。

三、嵌套对象映射(支持多层级转换)

当类中包含嵌套对象(例如 User 包含 Order 列表)时,MapStruct Plus 能自动支持嵌套转换,无需额外配置。对于更复杂的场景,也可以自定义嵌套规则。

示例:嵌套对象转换

// 嵌套实体类:Order
@Data
@AutoMapper(target = OrderDto.class)
public class Order {
    private Long orderId;
    private BigDecimal amount;
}

// 源类:User(包含 Order 列表)
@Data
@AutoMapper(target = UserWithOrderDto.class)
public class User {
    private String username;
    private List<Order> orderList; // 嵌套列表
}

// 目标类:UserWithOrderDto(嵌套 DTO)
@Data
public class UserWithOrderDto {
    private String username;
    private List<OrderDto> orderList; // 嵌套 DTO 列表
}

// 测试:自动完成嵌套转换
@Test
public void testNestedConvert() {
    User user = new User();
    user.setUsername(“jack”);

    Order order = new Order();
    order.setOrderId(1L);
    order.setAmount(new BigDecimal(“100.00”));
    user.setOrderList(Collections.singletonList(order));

    UserWithOrderDto dto = converter.convert(user, UserWithOrderDto.class);
    // 自动将 Order 列表转为 OrderDto 列表
    System.out.println(dto.getOrderList().get(0).getOrderId()); // 输出 1
}

四、枚举映射(轻松实现枚举转换)

实现枚举与基本类型(如 Integer、String)之间的转换,使用 @AutoEnumMapper 注解即可快速配置。

1. 基础枚举转换(枚举 ↔ 基本类型)

// 枚举类:指定唯一标识字段(state)
@Getter
@AllArgsConstructor
@AutoEnumMapper(“state”) // 标记枚举,指定映射字段
public enum GoodsStateEnum {
    ENABLED(1, “启用”),
    DISABLED(0, “禁用”);
    private final Integer state; // 映射字段
    private final String desc;
}

// 源类:Goods
@Data
@AutoMapper(target = GoodsVo.class)
public class Goods {
    private String goodsName;
    private GoodsStateEnum state; // 枚举字段
}

// 目标类:GoodsVo
@Data
public class GoodsVo {
    private String goodsName;
    private Integer state; // 枚举对应 Integer 类型
}

// 测试
@Test
public void testEnumConvert() {
    Goods goods = new Goods();
    goods.setGoodsName(“手机”);
    goods.setState(GoodsStateEnum.ENABLED);

    GoodsVo vo = converter.convert(goods, GoodsVo.class);
    assert vo.getState().equals(1); // 枚举 → Integer

    Goods newGoods = converter.convert(vo, Goods.class);
    assert newGoods.getState().equals(GoodsStateEnum.ENABLED); // Integer → 枚举
}

2. 跨模块枚举映射

当枚举类与使用它的类不在同一个模块时,可以在 @AutoMapper 注解中通过 useEnums 属性指定依赖的枚举。

@Data
@AutoMapper(target = GoodsVo.class, useEnums = {GoodsStateEnum.class})
public class Goods {
    private String goodsName;
    private GoodsStateEnum state;
}

五、自定义类型转换器

当内置的转换规则无法满足特定需求时,你可以轻松地自定义转换器。

示例:自定义字符串转 List 转换器

// 自定义转换器:String(逗号分隔)→ List<String>
@Component
public class StringToListString {
    public List<String> stringToListString(String str) {
        return StrUtil.split(str);
    }
}

// 应用转换器
@Data
@AutoMapper(target = UserDto.class, uses = StringToListStringConverter.class)
public class User {
    private String username;
    // 源字段:String(逗号分隔),目标字段:List<String>
    @AutoMapping(target = “hobbyList”)
    private String hobbies; // 例如:“篮球,足球,游泳”
}

@Data
public class UserDto {
    private String username;
    private List<String> hobbyList; // 转换后:[“篮球”,“足球”,“游泳”]
}

提示:当自定义的类型转换器中有多个方法时,可以通过 @AutoMappingqualifiedByName 属性来指定具体的转换方法。

💡 最佳实践

  1. 编译期报错优先排查:MapStruct Plus 在编译期生成代码,出现错误可以直接定位问题源头。常见原因包括字段名拼写错误、类型不匹配且未配置转换器。
  2. 版本建议:建议使用 1.4.0 及以上版本,以获得更多高级功能和稳定性提升。
  3. 反向转换控制:默认支持反向转换。如果确定不需要从目标类反向转换回源类,可以在 @AutoMapper 中设置 reverseConvertGenerate = false,以减少生成的代码量。

🎉 总结

MapStruct Plus 将 MapStruct 的“极简配置”理念发挥到了极致。无论是基础的对象映射,还是复杂的嵌套转换、枚举映射,都能通过简单的注解“一句搞定”。相比原生 MapStruct,它彻底省去了手动定义 Mapper 接口和编写转换方法的冗余工作,让开发者能够更专注于核心业务逻辑的构建。

如果你在SpringBoot项目中被大量的对象转换代码所困扰,不妨试试 MapStruct Plus,它或许能成为你提升开发效率的又一得力助手。想了解更多高级用法和详细配置,可以访问其官网 https://www.mapstruct.plus/ 进行探索。在 云栈社区 也有许多关于提升开发效率的实战讨论,欢迎交流分享。




上一篇:Java日志管理与排查:从基础到ELK的8个最佳实践
下一篇:Raft 共识算法习题精解:8道真题助你掌握日志与选举关键点
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-31 01:48 , Processed in 0.346809 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表