SpringBoot 对象转换 MapStruct

news2025/5/23 17:34:32

文章目录

    • 工作原理
    • 核心优势
    • 为什么不使用 `BeanUtils`
    • 使用步骤
      • 添加依赖
      • 定义实体类和VO类
      • 定义映射接口
      • 测试数据
    • 参考

工作原理

基于 Java 的 JSR 269 规范,该规范允许在编译期处理注解,也就是 Java 注解处理器。MapStruct 通过定义的注解处理器,在编译期读取映射接口,并生成相应的实现类。这个过程中,它会解析接口中声明的映射方法,并创建对应的 getters 和 setters 调用

核心优势

  1. 零反射:生成的代码直接调用对象的 getter/setter,性能接近手写代码。
  2. 编译时检查:映射错误(如字段不匹配)在编译时暴露,而非运行时。
  3. 灵活性:支持自定义转换逻辑、嵌套对象映射、集合转换等。
  4. 与 IDE 兼容:生成的代码可调试,便于跟踪问题。

为什么不使用 BeanUtils

在高并发的场景中,性能是最为重要的,BeanUtils 虽然可以快速完成 JavaBean 之间的转换,但是底层逻辑是基于反射实现的,这样会导致在高并发场景中性能下降,这时候最高效的处理办法就是手动的 getter/setter,但是要大量处理这些可重复的操作会浪费大量时间,因此可以使用 MapStruct 解决

区别:

  • 编译时生成代码 vs 运行时反射MapStruct生成的映射代码是在编译时生成的,而BeanUtils则是在运行时使用反射机制实现转换
  • 性能和可扩展性:由于 MapStruct 生成的代码是类型安全的,因此可以比使用反射更加高效和可靠。同时,MapStruct 还能够自定义转换逻辑并支持扩展,使得它更加灵活和可扩展。
  • 集成方式:MapStruct 可以无缝集成到 Spring 中,也可以与其他 IoC 容器结合使用;而 BeanUtils 是 Spring 框架自带的工具类。
  • 映射规则的定义方式:MapStruct 使用基于注解的方式在源代码中定义映射规则,而 BeanUtils 则需要手动编写复杂的转换方法。

使用步骤

添加依赖

pom.xml 中添加依赖

<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>
  • mapstruct 依赖
    • 提供了 MapStruct 的核心注解,如 @Mapper@Mapping,用于定义映射接口
    • 在运行时,如果使用默认的组件模型(default),还需要依赖 Mappers.getMapper(…) 方法来获取映射器实例
  • mapstruct-processor 依赖
    • 注解处理器(annotation processor),在编译阶段扫描带有 MapStruct 注解的接口,并生成对应的实现类
    • 不会在运行时参与应用程序的执行,因此通常不需要在运行时包含此依赖

根据 mapstruct-processor 依赖的定义,其实不应该将该依赖放在 dependencies 标签中,而是将 mapstruct-processor 作为注解处理器添加到 maven-compiler-plugin 的 annotationProcessorPaths 中,这也是官方推荐处理

...
<properties>
    <org.mapstruct.version>1.6.3</org.mapstruct.version>
</properties>
...
<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</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.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>
...

如果是在已经有 Lombok 依赖的项目中加入 mapstruct 依赖需要注意这两个注解处理器的配置,不然在 mvn install 等命令执行时可能会发生关于 Lombok 依赖相关的错误

Lombok 1.18.16 引入了一个破坏性更改(变更日志)。必须添加额外的注解处理器 lombok-mapstruct-binding (Maven),否则 MapStruct 将无法与 Lombok 兼容。

<build>
   <plugins>
	  <plugin>
		 <groupId>org.apache.maven.plugins</groupId>
		 <artifactId>maven-compiler-plugin</artifactId>
		 <version>3.13.0</version>
		 <configuration>
			<source>17</source>
			<target>17</target>
			<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>
			    	<!-- Lombok 版本从 1.18.16 开始必须添加,其他版本为可选 -->
			    	<!-- Lombok 与 MapStruct 的绑定处理器 -->
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok-mapstruct-binding</artifactId>
                        <version>0.2.0</version>
                    </path>
			</annotationProcessorPaths>
		 </configuration>
	  </plugin>
   </plugins>
</build>

定义实体类和VO类

// 实体类
@Data  
@Accessors(chain = true)  
public class Student implements Serializable {  
  
    private Long  id;  
  
    private String name;  
  
    private Integer age;  
  
    private String studentNo;  
  
}

// VO类
@Data  
@Accessors(chain = true)  
public class StudentVO implements Serializable {  
  
    private Long voId;  
  
    private String voName;  
  
    private Integer voAge;  
  
    private String voStudentNo;  
  
}

定义映射接口

定义抽象接口

@MapperConfig  
public interface IMapping<SOURCE, TARGET> {  
  
    TARGET sourceToTarget(SOURCE source);  

    // 反向映射(需配置反向方法)
    @InheritInverseConfiguration(name = "sourceToTarget")  
    SOURCE targetToSource(TARGET target);  
  
    @InheritConfiguration(name = "sourceToTarget")  
    List<TARGET> sourceToTarget(List<SOURCE> sourceList);  
  
    @InheritConfiguration(name = "sourceToTarget")  
    List<SOURCE> targetToSource(List<TARGET> targetList);  
  
    List<TARGET> sourceToTarget(Stream<SOURCE> stream);  
  
    List<SOURCE> targetToSource(Stream<TARGET> stream);  
  
}

定义映射接口,@Mapper(componentModel = "spring")。默认情况下,mapstruct 生成的 Mapper 实现类不会被 Spring 容器管理。如果不指定 componentModel,需要通过 StudentMapping mapper = Mappers.getMapper(StudentMapping.class) 手动获取 Mapper 实例。

通过设置 componentModel = "spring"mapstruct 会在生成的实现类上添加 @Component 注解,使其成为 Spring 管理的 Bean,从而可以在其他组件中通过依赖注入方式使用。因此,StudentMapping 接口通过 @Mapper(componentModel = "spring") 注解,其实现类被注册为 SpringBean,可以通过构造函数注入方式使用该 Mapper

@Mapper(componentModel = "spring")  
public interface StudentMapping extends IMapping<Student, StudentVO>{  
  
    @Override  
    @Mapping(source = "id", target = "voId")  
    @Mapping(source = "name", target = "voName")  
    @Mapping(source = "age", target = "voAge")  
    @Mapping(source = "studentNo", target = "voStudentNo")  
    StudentVO sourceToTarget(Student student);  
  
    @Override  
    List<StudentVO> sourceToTarget(List<Student> students);  
}

测试数据

默认情况下,Spring 在实例化测试类时使用无参构造函数,并通过字段注入(@Autowired)方式注入依赖。而使用 @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) 注解后,Spring 会尝试使用带参数的构造函数,并通过构造函数注入方式自动注入所需的依赖,也就是可以使用 Lombok 依赖中的 @RequiredArgsConstructor 注解

@SpringBootTest  
@RequiredArgsConstructor  
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)  
public class StudentMapStructTest {  
  
    private final StudentMapping studentMapping;  
  
    @Test  
    public void test() {  
        Student student = new Student();  
        student.setId(1L);  
        student.setName("Mayer");  
        student.setAge(18);  
        student.setStudentNo("20250001");  
  
        StudentVO studentVO = studentMapping.sourceToTarget(student);  
        System.out.println(studentVO);  
        System.out.println(studentMapping.targetToSource(studentVO));  
    }  
  
}

打印结果

StudentVO(voId=1, voName=Mayer, voAge=18, voStudentNo=20250001)
Student(id=1, name=Mayer, age=18, studentNo=20250001)

参考

  • Java-Mapstruct 实践 | 无垠之境
  • Java Review - MapStruct 全掌握:8 个案例探究高效快捷的 Java 对象映射 - CharyGao - 博客园
  • 【MapStruct】还在用BeanUtils?不如试试MapStruct - CharyGao - 博客园

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2384047.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

01-jenkins学习之旅-window-下载-安装-安装后设置向导

1 jenkins简介 百度百科介绍&#xff1a;Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件项目可以进行持续集成。 [1] Jenkins官网地址 翻译&…

免费开放试乘体验!苏州金龙自动驾驶巴士即将上线阳澄数谷

近日&#xff0c;苏州自动驾驶巴士线路——阳澄数谷示范线正式上线&#xff0c;即日起向全民免费开放试乘体验&#xff01; 在苏州工业园区地铁3号线倪浜•阳澄数谷站外&#xff0c;一辆辆黑、白配色的小巴正在道路上有条不紊地行驶。与普通公交不同的是&#xff0c;小巴造型奇…

每日Prompt:像素风格插画

提示词 像素风格插画&#xff0c;日式漫画脸&#xff0c;画面主体为一位站在路边的男孩&#xff0c;人物穿着黑色冲锋衣&#xff0c;手里拿着手机&#xff0c;男孩靠坐在机车旁边&#xff0c;脚边依偎着一只带着小摩托车头盔的小小猫&#xff0c;背景是雨中&#xff0c;身旁停…

Windows逆向工程提升之二进制分析工具:HEX查看与对比技术

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 十六进制查看工具 应用于逆向工程的知识点 ​编辑 二进制对比工具 应用于逆向工程的知识点 十六进制查看工具 十六进制查看器是逆向工程的基础工具&#xff0c;它可以以十六进制格式…

2024游戏安全白皮书:对抗激烈!PC游戏外挂功能数增长超149%,超85%移动外挂为定制挂(附获取方式)

2024 年&#xff0c;中国游戏市场实际销售收入达 3257.83 亿元&#xff0c;同比增长 7.53%&#xff1b;用户规模 6.74 亿人&#xff0c;同比增长 0.94%&#xff0c;再创新高。这份庞大的数据背后&#xff0c;更是对安全防线实力的严峻拷问。 在广东省游戏产业协会的指导下&…

12kV 环保气体绝缘交流金属封闭开关设备现场交流耐压试验规范

范围 本文件规定了12kV环保气体绝缘交流金属封闭开关设备现场交流耐压试验的被试设备及试验接线、试验条件、试验步骤、试验判据及异常处理方法。 本文件适用于12kV环保气体绝缘交流金属封闭开关设备现场交流耐压试验&#xff0c;其他气体绝缘交流金属封闭开关设备可参照执行。…

位图算法——判断唯一字符

这道题有多种解法&#xff0c;可以创建hash数组建立映射关系判断&#xff0c;但不用新的数据结构会加分&#xff0c;因此我们有“加分”办法——用位图。 我们可以创建一个整型变量&#xff08;32位&#xff09;而一共才26个字母&#xff0c;所以我们只要用到0-25位即可&#…

【UE5】环形菜单教程

效果 步骤 1. 下载图片资源&#xff1a;百度网盘 请输入提取码 提取码:fjjx 2. 将图片资源导入工程&#xff0c;如下 3. 新建3个控件蓝图&#xff0c;这里分别命名为“WBP_CircularMenu”、“WBP_Highlight”、“WBP_Icon” 4. 打开“WBP_Icon”&#xff0c;设置“所需” 添加…

Athena 执行引擎:在线服务计算的效率王者

引言 在在线服务领域&#xff0c;计算任务呈现出独特的特性&#xff1a;一方面&#xff0c;数据量通常不会过于庞大&#xff0c;因为在线服务对耗时和响应速度有着严苛要求&#xff1b;另一方面&#xff0c;计算任务具有可控性&#xff0c;其大多并非由用户实时输入动态生成&a…

飞桨paddle ‘ParallelEnv‘ object has no attribute ‘_device_id‘【已解决】

书借上回&#xff0c;自从我反复重装paddle之后&#xff0c;我发现了&#xff0c;只要pip list中有库&#xff0c;但是代码报错&#xff0c;那就是飞桨没把代码更新完全&#xff0c;只能自己去改源代码 我又遇到报错了&#xff1a; 根据报错信息&#xff0c;找到ParallelEnv报…

微信小程序之Promise-Promise初始用

我们来尝试使用Promise。 1、需求&#xff0c;做个抽奖的按钮&#xff0c; 抽奖规则&#xff1a; 30%的几率中奖&#xff0c;中奖会提示恭喜恭喜&#xff0c;奖品为10万 RMB 劳斯莱斯优惠券&#xff0c;没中奖会提示再接再厉。 2、先搭界面&#xff1a; <view class&qu…

准备好,开始构建:由 Elasticsearch 向量数据库驱动的 Red Hat OpenShift AI 应用程序

作者&#xff1a;来自 Elastic Tom Potoma Elasticsearch 向量数据库现在被 “基于 LLM 和 RAG 的 AI 生成” 验证模式支持。本文将指导你如何开始使用。 Elasticsearch 已原生集成业内领先的生成式 AI 工具和服务提供商。欢迎观看我们的网络研讨会&#xff0c;了解如何突破 RA…

RNN神经网络

RNN神经网络 1-核心知识 1-解释RNN神经网络2-RNN和传统的神经网络有什么区别&#xff1f;3-RNN和LSTM有什么区别&#xff1f;4-transformer的归一化有哪几种实现方式 2-知识问答 1-解释RNN神经网络 Why&#xff1a;与我何干&#xff1f; 在我们的生活中&#xff0c;很多事情…

Linux | 开机自启动设置多场景实现

注&#xff1a;本文为“Llinux 设置开机自启”相关文章合辑。 略作重排&#xff0c;未整理去重。 如有内容异常&#xff0c;请看原文。 Linux 设置开机自启动的三种方法 幽夜卡尔 2022-10-22 一、在 /etc/rc.local 文件中添加自启动命令 编辑文件&#xff1a;执行以下命令&a…

杨校老师竞赛课之青科赛GOC3-4年级组模拟题

1.山峰&#xff08;程序填空&#xff09;程序填空题 题目描述 编程画出山峰。 要求&#xff1a;两个正三角形&#xff0c;三角形边长分别是200、100&#xff0c;山峰的颜色为8号色。 int main() {// 绘制等边三角形&#xff08;边长100&#xff09;&#xff1a;右转30度调整…

SC3000智能相机-自动存图

1、需求:SC3000智能相机开机自动存图。相机自带的相机存储空间有限,预留存图需要开启SCMVS、并手动点存图。如果工人忘了开启则不会存图,导致生产严重失误! 2、方法:利用相机提供的FTP协议,将图自动存到本地。 1、在本地建立FTP服务器。 (1)win10默认开启了FTP服务器…

【Java高阶面经:微服务篇】5.限流实战:高并发系统流量治理全攻略

一、限流阈值的三维度计算模型 1.1 系统容量基准线:压测驱动的安全水位 1.1.1 压力测试方法论 测试目标:确定系统在资源安全水位(CPU≤80%,内存≤70%,RT≤500ms)下的最大处理能力测试工具: 单机压测:JMeter(模拟10万并发)、wrk(低资源消耗)集群压测:LoadRunner …

2025中青杯数学建模B题思路+模型+代码

本文将为大家带来2025年中青杯的选题建议&#xff0c;旨在十分钟内帮助大家快速了解每个题目具体难点、涉及模型等。初步预估赛题难度 A:B:C4:5:3初步预测选题人数 A:B:C2:1:0.6 首先是C题&#xff0c;忧郁症的双重防线&#xff1a;精准预测与有效治疗&#xff0c;这个题目涉及…

记录:uniapp 上线部署到微信小程序vendorjs包过大的问题

问题&#xff1a; 在代码依赖分析图中&#xff0c;可以看到主包的容量已经超过了2M了&#xff0c;分包没有超&#xff01; 根据网上的资料的解决方案&#xff0c;当前我已经做了以下相关的配置&#xff1a; 1.分包 2.在manifest.json的&#xff08;mp-weixin&#xff09;节点…

asp.net core 添加 EntityFrame

1&#xff1a;Nuget 引入程序集 Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCore.SqlServer Microsoft.EntityFrameworkCore.SqlServer.Design Microsoft.EntityFrameworkCore.Tools 2&#xff1a;执行脚本 Scaffold-DbContext "Data Source.;Initial Ca…