几分钟了解下MapStruct的实现原理

一: 前言

最近项目上打算使用MapStruct框架来做JavaBean的映射,所以对MapStruct框架做了一个整体的探究,希望大家看完后能够对MapStruct框架有个整体上的了解。

二: 基本介绍

为了文章的严谨,我想借助于官网的介绍再合适不过了:

  • 是什么? MapStruct 是一个代码生成器,它基于约定而非配置方法,极大地简化了 Java Bean 类型之间映射的实现。 生成的映射代码使用普通方法调用,因此快速、类型安全且易于理解。
  • 为什么? 多层应用程序通常需要在不同的对象模型(例如实体和 DTO)之间进行映射。编写这样的映射代码是一项繁琐且容易出错的任务。MapStruct 旨在通过尽可能自动化来简化这项工作。 与其他映射框架相比,MapStruct 在编译时生成 Bean 映射,这确保了高性能,允许快速的开发人员反馈和彻底的错误检查。
  • 怎么做的? MapStruct 是一个注释处理器,它插入到 Java 编译器中,可以在命令行构建(Maven、Gradle 等)中使用,也可以在您喜欢的 IDE 中使用。

三: 技术点掌握

Java编译过程

我们都知道java编译将 ".java" 文件转换为 ".class" 字节码文件,最终 ".class" 字节码文件运行在jvm虚拟机之上,那java编译的过程是怎样的呢?下面简单用一张图来表示:

JSR269

用红色背景填充的"注解处理 "步骤就是我们要来说的JSR269规范:

  • 在JDK1.5之后,Java语言提供了对注解(Annotation)的支持,这些注解与普通的Java代码一样,是在运行期间发挥作用的。在JDK1.6中实现了JSR-269规范,JSR-269:Pluggable Annotations Processing API(插入式注解处理API)。提供了一组插入式注解处理器的标准API在编译期间对注解进行处理。我们可以把它看做是一组编译器的插件,在这些插件里面,可以读取、修改、添加抽象语法树中的任意元素。如果这些插件在处理注解期间对语法树进行了修改,编译器将回到解析及填充符号表的过程重新处理,直到所有插入式注解处理器都没有再对语法树进行修改为止,每一次循环称为一个Round,这是一个回环过程。
  • 并且在此阶段,如果项目中有注解处理器想生成新的源文件。可使用JavacFiler创建新的源文件,这些源文件在此时被加入到编译过程中。它们被视为项目源代码的一部分,并将在接下来的步骤中一起被编译。

使用Freemarker模板文件快速生成java类文件

Freemarker是一款模板引擎:即一种基于模板和要改变的数据,用来输出文本(HTML网页、电子邮件),2002年编写者Attila在freemarker.ext*包里面支持了完成对javabean、jython、XML的映射。因此我们可以借助于这项模板引擎技术快速生成我们的java类。

实战

在这里我给大家找了两个可以快速入手上面技术点的实战例子,有兴趣的可以动起手来实操一个简单的demo体验下这些知识点:

JSR269实践

使用freemaker生成javabean

解析MapStruct实现

MapStcurt是在编译过程中生成类,我们这里是使用的是Maven包管理工具,Maven专门有命令(MvnDebug)提供给我们做编译时候的调试,然后我们可以通过idea配置远程调试看MapStruct的处理过程了。

准备调试环境

笔者这里演示使用的JDK是11版本,MapStruct是1.5.3.Final版本

运行MvnDebug命令

找到MapStruct注解处理类,在process方法上面打上断点

org.mapstruct.ap.MappingProcessor#process(java.util.Set<? extends javax.lang.model.element.TypeElement>, javax.annotation.processing.RoundEnvironment):

配置远程调试

探究MapStruct实现过程

点击调试按钮,即可进入断点:

1: 我们在源码META-INF/services目录下已经看到MapStruct配置的注解处理器了,所以它是基于JSR269进行实现的无可厚非。

2: 在断点org.mapstruct.ap.internal.writer.FreeMarkerModelElementWriter#write处,我们可以看到基于FreeMarker创建javabean:

2.1: 利用javacFiler创建sourceFile(后续会编译为.class字节码文件): org.mapstruct.ap.internal.processor.MapperRenderingProcessor#createSourceFile

2.2: 利用freemarker生成javabean:

org.mapstruct.ap.internal.writer.ModelWriter#writeModel

org.mapstruct.ap.internal.writer.FreeMarkerModelElementWriter#write

3: 如何获得这个生成类? 我们在使用MapStruct的使用方法org.mapstruct.factory.Mappers#getMapper(java.lang.Class)获取实现类,其内部其实是加载编译好的实现类,然后返回实现类:

org.mapstruct.factory.Mappers#doGetMapper

最后

笔者这里简单介绍了MapStruct的实现原理,大家可以想想这种方案做出来的javabean映射和我们经常使用的commons、spring里面的转换工具有啥不一样呢?又有哪些技术是基于JSR269做的呢?

相关推荐
用户835629078051几秒前
使用 Python 设置 Excel 页眉和页脚
后端·python
步行cgn3 分钟前
IoC 控制反转:从概念到 Spring 的实现
后端
爱勇宝18 分钟前
没有 Fn 键关触控板?我做了一个双击即用的 Windows 小工具
前端·后端·程序员
沙湖遇雨1 小时前
8.ByteBuf 的数据模型、生命周期和内存分配
后端
by组态1 小时前
Ricon组态系统API参考手册
前端·后端·物联网
再吃一根胡萝卜1 小时前
Agent 如何选择与生成 SQL —— Text2SQL 全链路详解
后端
苍何1 小时前
把操作流程直接录成 Skill,豆包太夯了!
后端
Go_error2 小时前
在 Go 服务层中如何优雅地管理事务?
后端·go