几分钟了解下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做的呢?

相关推荐
customer082 小时前
【开源免费】基于SpringBoot+Vue.JS体育馆管理系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
计算机-秋大田4 小时前
基于微信小程序的电子竞技信息交流平台设计与实现(LW+源码+讲解)
spring boot·后端·微信小程序·小程序·课程设计
加油,旭杏6 小时前
【go语言】接口
开发语言·后端·golang
谢大旭7 小时前
ASP.NET Core 中间件
后端·中间件·c#
customer087 小时前
【开源免费】基于SpringBoot+Vue.JS景区民宿预约系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
uzong8 小时前
Java函数式接口:代码艺术的诗意绽放
后端
HelloDam9 小时前
基于 mzt-biz-log 实现接口调用日志记录
后端
SomeB1oody9 小时前
【Rust自学】15.6. RefCell与内部可变性:“摆脱”安全性限制
开发语言·后端·rust
兮动人12 小时前
Golang 执行流程分析
开发语言·后端·golang·golang 执行流程分析