mapStruct笔记
背景
mapStruct 是一个方便对象转换的工具,类似的工具还有 Dozer, BeanUtils。
实现
mapStruct的核心是在编译期生成基于转换规则的 Impl 文件,运行时直接调用 Impl 文件中的函数。整个 mapStruct 分成三个部分:
自定义注解,指定转换的规则。例如 source, target 等。
freemarker 模板,用来生成 impl 文件。
基于
javax.annotation.processing的处理模块。
基本流程是
color: rgb(0, 0, 0);
font: normal normal 400 normal 14px / 25.2px Verdana, Arial, Helvetica, sans-serif;
}
具体解析
具体的解析逻辑是将解析注解内容转化为 Mapper model 对象,然后将 Mapper model 写入 freemarker 模板中。
处理框架
整个注解的解析是通过 java compile[1] 实现的,逻辑包含在MappingProcessor.process 函数中,并通过 MapperGenerationVisitor 进行解析。
@Override
public boolean process(
final Set<? extends TypeElement> annotations,
final RoundEnvironment roundEnvironment) {
// 遍历需要处理的注解
for ( TypeElement oneAnnotation : annotations ) {
<span class="hljs-comment">//Indicates that the annotation's type isn't on the class path of the compiled</span>
<span class="hljs-comment">//project. Let the compiler deal with that and print an appropriate error.</span>
<span class="hljs-keyword">if</span> ( oneAnnotation.getKind() != ElementKind.ANNOTATION_TYPE ) {
<span class="hljs-keyword">continue</span>;
}
// 遍历包含 Mapper 注解的 interface and class , 例如 org.mapstruct.ap.test.conversion.SourceTargetMapper
for ( Element oneAnnotatedElement : roundEnvironment.getElementsAnnotatedWith( oneAnnotation ) ) {
// MapperGenerationVisitor 解析每个Mapper 注解的内容 成为一个 Model
oneAnnotatedElement.accept( new MapperGenerationVisitor( processingEnv ), null );
}
}
<span class="hljs-keyword">return</span> ANNOTATIONS_CLAIMED_EXCLUSIVELY;
}
解析逻辑
MapperGenerationVisitor 负责解析注解为 Mapper model, 并写入 ftl 模板文件中。
MapperGenerationVisitor.retrieveModel 包含了具体的解析逻辑,将注解内容转化为 Mapper Model。
ModelWriter 负责将 Mapper Model 写入 ftl 模板中。
整个逻辑都是围绕 Mapper model 展开的, Mapper 包含如下内容:
private final String packageName; // 包的名称
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String interfaceName; <span class="hljs-comment">// 接口名称</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String implementationName; <span class="hljs-comment">// 应用名称</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> List<BeanMapping> beanMappings; <span class="hljs-comment">// 一系列的 mapping 信息, 每个 method 对应一个 BeanMapping</span>
每一个 BeanMapping 对应一个转换函数,它的格式如下:
private final Type sourceType; // 函数的输入参数类型
private final Type targetType; // 函数的结果参数类型
private final List<PropertyMapping> propertyMappings; // 转换函数的每个属性的信息
private final MappingMethod mappingMethod; // 映射的函数
private final MappingMethod reverseMappingMethod; // 翻转映射的函数
private final boolean isIterableMapping; // 是不是迭代
例如 SourceTargetMapper 接口:
@Mapper
public interface SourceTargetMapper {
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
<span class="hljs-meta">@Mappings</span>({
<span class="hljs-meta">@Mapping</span>(source = <span class="hljs-string">"qax"</span>, target = <span class="hljs-string">"baz"</span>),
<span class="hljs-meta">@Mapping</span>(source = <span class="hljs-string">"baz"</span>, target = <span class="hljs-string">"qax"</span>)
})
<span class="hljs-function">Target <span class="hljs-title">sourceToTarget</span><span class="hljs-params">(Source source)</span></span>;
<span class="hljs-function">Source <span class="hljs-title">targetToSource</span><span class="hljs-params">(Target target)</span></span>;
}
映射为 Mapper Model 为:
{
"beanMappings":[
{
"iterableMapping":false,
"mappingMethod":{
"name":"sourceToTarget",
"parameterName":"source"
},
"propertyMappings":[
{
"fromConversion":"target.getFoo().intValue()",
"sourceName":"foo",
"sourceType":{
"name":"int",
"primitive":true
},
"targetName":"foo",
"targetType":{
"name":"Long",
"packageName":"java.lang",
"primitive":false
},
"toConversion":"Long.valueOf( source.getFoo() )"
},
Object{...},
Object{...},
Object{...},
Object{...}
],
"reverseMappingMethod":{
"name":"targetToSource",
"parameterName":"target"
},
"sourceType":{
"name":"Source",
"packageName":"org.mapstruct.ap.test.conversion",
"primitive":false
},
"targetType":{
"name":"Target",
"packageName":"org.mapstruct.ap.test.conversion",
"primitive":false
}
}
],
"implementationName":"SourceTargetMapperImpl",
"interfaceName":"SourceTargetMapper",
"packageName":"org.mapstruct.ap.test.conversion"
}
写入模板
写入模板是使用 freemarker 进行编写的,最初写入逻辑很简单,直接使用 ModelWriter 进行写入。ftl 模板的部分内容如下:
package ${packageName};
import java.util.ArrayList;
import java.util.List;
public class ${implementationName} implements ${interfaceName} {
上面的 ${packageName}对应的就是 Mapper Model 中的 packageName。
参考
原文地址:https://www.cnblogs.com/SpeakSoftlyLove/p/9794661.html
mapStruct笔记的更多相关文章
- Java实体映射工具MapStruct的使用
官网地址:http://mapstruct.org/ MapStruct 是一个代码生成器,简化了不同的 Java Bean 之间映射的处理,所谓的映射指的就是从一个实体变化成一个实体.例如我们在实际 ...
- mapstruct 快速使用
mapstruct 快速使用 mapstruct 主要的作用则是用来复制对象字段使用,功能非常的强大.在没有使用 mapstruct 之前可能都在使用 BeanUtils ,但是 BeanUtils ...
- git-简单流程(学习笔记)
这是阅读廖雪峰的官方网站的笔记,用于自己以后回看 1.进入项目文件夹 初始化一个Git仓库,使用git init命令. 添加文件到Git仓库,分两步: 第一步,使用命令git add <file ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- SQL Server技术内幕笔记合集
SQL Server技术内幕笔记合集 发这一篇文章主要是方便大家找到我的笔记入口,方便大家o(∩_∩)o Microsoft SQL Server 6.5 技术内幕 笔记http://www.cnbl ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- NET Core-学习笔记(三)
这里将要和大家分享的是学习总结第三篇:首先感慨一下这周跟随netcore官网学习是遇到的一些问题: a.官网的英文版教程使用的部分nuget包和我当时安装的最新包版本不一致,所以没法按照教材上给出的列 ...
- springMVC学习笔记--知识点总结1
以下是学习springmvc框架时的笔记整理: 结果跳转方式 1.设置ModelAndView,根据view的名称,和视图渲染器跳转到指定的页面. 比如jsp的视图渲染器是如下配置的: <!-- ...
随机推荐
- Git之fatal: remote origin already exists
文件提交到远程分支,我们需要提前表明需要提交到哪个远程分支 比如:git remote add origin git@github.com:wqk66/test.git,表示他提交到远程仓库test ...
- Spring-bean(一)
配置形式:基于xml文件的方式:基于注解的方式 Bean的配置方式:通过全类名(反射),通过工厂方法(静态工厂方法&实例工厂方法),FactoryBean 依赖注入的方式:属性注入,构造器注入 ...
- 【学习笔记】二:在HTML中使用JavaScript
1.<script>标签 1)考虑到最大限度的浏览器兼容性和约定俗成,type属性使用:text/javascript. 2)标签建议放置到</body>标签前,提高用户体验( ...
- 程序员面试系列之Java单例模式的攻击与防御
我写的程序员面试系列 Java面试系列-webapp文件夹和WebContent文件夹的区别? 程序员面试系列:Spring MVC能响应HTTP请求的原因? Java程序员面试系列-什么是Java ...
- SQLite – ORDER 子句
SQLite - ORDER BY子句 The SQLite ORDER BY子句用于数据按升序或降序排序,基于一个或多个列. 语法: ORDER BY子句的基本语法如下: SELECT column ...
- 常用的-->查找算法与排序算法
顺序查找 从列表第一个元素开始,顺序进行搜索,直到找到为止. 二分查找 从有序列表的候选区data[0:n]开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一半. li = [1, 2, ...
- 朴素贝叶斯法(naive Bayes)
<统计学习方法>(第二版)第4章 4 朴素贝叶斯法 生成模型 4.1 学习与分类 基于特征条件独立假设学习输入输出的联合概率分布 基于联合概率分布,利用贝叶斯定理求出后验概率最大的输出 条 ...
- PHP20 PHP面向对象辅助
学习要点 常用函数 命名空间 类的自动加载 常用函数 class_exists 作用:检查类是否已定义 语法格式: bool class_exists ( string $class_name [, ...
- PHP09 字符串和正则表达式
学习要点 字符串处理简介 常用的字符串输出函数 常用的字符串格式化函数 字符串比较函数 正则表达式简介 正则表达式语法规则 与perl兼容的正则表达式函数 字符串处理介绍 Web开发中字符串处理 ...
- 阿里云ECS搭建node/mongodb开发环境及部署
一.前端的er在window或mac上安装开发环境应该再清楚不过了.但在服务器上安装还是有点不同的,毕竟是 centOS,从此不得不走上用命令操作…… 二.前期准备 1.首先,我们去阿里云网站阿里云服 ...