MapStruct是什么?

MapStruct is a code generator that greatly simplifies the implementation of mappings between Java bean types based on a convention over configuration approach.——https://mapstruct.org/

从官方定义来看,MapStruct类似于我们熟悉的BeanUtils, 是一个Bean的转换框架。

In contrast to other mapping frameworks MapStruct generates bean mappings at compile-time which ensures a high performance, allows for fast developer feedback and thorough error checking.——https://mapstruct.org/

他与BeanUtils最大的不同之处在于,其并不是在程序运行过程中通过反射进行字段复制的,而是在编译期生成用于字段复制的代码(类似于Lombok生成get()和set()方法),这种特性使得该框架在运行时相比于BeanUtils有很大的性能提升。

lombok

这个大家都很熟悉,生成getset方法,那么mapstuct要依赖这种方法。

MapStuct与lombok的引入的正确关系

由于MapStruct和Lombok都会在编译期生成代码,如果配置不当,则会产生冲突,因此在工程中同时使用这两个包时,应该按照以下方案导入:

当POM中不包含Lombok时

<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.2.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.2.Final</version>
</dependency>

当POM中包含Lombok且不包含时

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency> <dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.2.Final</version>
</dependency> <dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.2.Final</version>
</dependency>

注意:引入时,mapstruct-processor必须lombok后面。

当POM中包含Lombok且包含时

<properties>
<org.mapstruct.version>1.5.2.Final</org.mapstruct.version>
</properties> <dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>8</source>
<target>8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>

问题产生

如果lombok在mapstuct后面,则会产生问题

<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.0.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.0.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>

mapstruct编译原理

为了探究上述问题产生的原因,我们首先要理解MapStruct的基本原理。

MapStruct与其他Bean映射库最大的不同就是,其在编译期间生成转换代码,而不是在运行时通过反射生成代码。

为了更直观的理解这一点,可以从target中找到MapStruct自动生成的对应的ConveterImpl类



即MapStruct为我们编写的Convert抽象类自动生成了一个实现。

而Lombok也是在编译时自动生成代码,那么问题大概率就出现在这里了。

MapStruct是如何与Lombok共存的?

查阅MapStruct官方文档可以发现这样一段内容:

其中提到,MapStruct的annotation processor必须在Lombok的annotation processor生成完代码之后,才可以正常运行。

所以,这应该就是在导入dependencies时,必须先导入Lombok包,再导入MapStruct-processor包才可以正常运行的原因了。不过还有个问题没有解决:

Maven到底在哪里规定了annotation processor的载入顺序?难道每次创建工程时,必须记住这些包导入顺序么?

MapStruct官方推荐的导入流程

在进一步查看MapStruct官网时发现,其并没有将MapStruct-processor放在dependencies中,而是放在了annotationProcessorPaths层级下:

https://mapstruct.org/documentation/installation/

...



<org.mapstruct.version>1.5.2.Final</org.mapstruct.version>



...





org.mapstruct

mapstruct

${org.mapstruct.version}





...







org.apache.maven.plugins

maven-compiler-plugin

3.8.1



1.8

1.8

                <path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>

这又是为什么呢?

查阅Maven官方文档,对于有这样一段描述:

If specified, the compiler will detect annotation processors only in those classpath elements. If omitted, the default classpath is used to detect annotation processors. The detection itself depends on the configuration of annotationProcessors.

即如果有层级,则使用这个层级声明注解处理器的顺序执行,如果没有,则按照默认classpath的顺序来使用注解处理器。

地址:https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#annotationProcessorPaths

地址:https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#annotationProcessorPaths

我们接下来以下命令来获取当前Maven项目中的classpath:

mvn dependency:build-classpath -Dmdep.outputFile=classPath.txt

从导出内容可以看出,classPath中的Jar包顺序就是与dependencies中导入的顺序是相同的。

自此,关于MapStruct导入顺序的所有问题均已经被解决,总结如下:

在POM中没有annotationProcessorPaths时,Maven使用的classPath作为注解处理器执行的顺序,而classPath的顺序正是dependencies中导入的顺序。

当MapStruct依赖在Lombok依赖前面时,在执行注解处理器期间, 由于Lombok还未生成get、set代码,因此在MapStruct看来,这些类并没有公开的成员变量,也就无从生成用于转换的方法。

在使用annotationProcessorPaths后,其强制规定了注解处理器的顺序,dependencies中的顺序就被忽略了,Maven一定会先运行Lombok再运行MapStruct,代码即可正常运行。

MapStruct与lombok加载顺序问题与annotationProcessorPaths的关系?的更多相关文章

  1. web.xml加载顺序

    一 1.启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取<listener>和<context-param>两个结点. 2.紧急着,容创建一个Ser ...

  2. web.xml文件加载顺序

    1.启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取<listener>和<context-param>两个结点. 2.紧急着,容创建一个Servl ...

  3. web.xml 中的listener、 filter、servlet 加载顺序及其详解

    在项目中总会遇到一些关于加载的优先级问题,近期也同样遇到过类似的,所以自己查找资料总结了下,下面有些是转载其他人的,毕竟人家写的不错,自己也就不重复造轮子了,只是略加点了自己的修饰. 首先可以肯定的是 ...

  4. css样式加载顺序及覆盖顺序深入理解

    注:内容转载 很多的新手朋友们对css样式加载顺序和覆盖顺序的理解有所偏差,下面用示例为大家详细的介绍下,感兴趣的朋友不要错过 { height: 100%; width: 200; position ...

  5. Java---类加载机制,构造方法,静态变量,(静态)代码块,父类,变量加载顺序

    直接上代码: 代码1: public class ConstroctTest { private static ConstroctTest test = new ConstroctTest(); // ...

  6. DOM加载顺序

    最近一直在困扰dom的加载顺序问题,经常会遇到以为绑定好的事件不响应等情况,一头雾水,直到请教了周围的同事,才发现了解dom的加载顺序是多么的重要. 关于这个问题,其实网上已经有一些介绍,但是我觉得并 ...

  7. PHP 依赖注入,从此不再考虑加载顺序

    说这个话题之前先讲一个比较高端的思想--'依赖倒置原则' "依赖倒置是一种软件设计思想,在传统软件中,上层代码依赖于下层代码,当下层代码有所改动时,上层代码也要相应进行改动,因此维护成本较高 ...

  8. MVC中 _ViewStart _Layout Index三个页面中的加载顺序

    MVC学习中忽然想到一个问题.. 在访问一个Index.cshtml页面时, MVC的加载顺序是怎么样的呢? 首先说下我的结论 . _ViewStart.cshtml . Index.cshtml . ...

  9. WebForm中搭配母版页和用户控件页时候的事件加载顺序

    在生产环境中,一个内容页(aspx)可能会包含数个用户控件(ascx),而每个控件可能都会涉及到数据库访问. 如果在内容页.母版页.控件页中各自使用自己的数据库访问方法,会造成很大的运行成本. 这样的 ...

  10. 详解web.xml中元素的加载顺序

    一.背景 最近在项目中遇到了启动时出现加载service注解注入失败的问题,后来经过不懈努力发现了是因为web.xml配置文件中的元素加载顺序导致的,那么就抽空研究了以下tomcat在启动时web.x ...

随机推荐

  1. 利用 Nginx 反向代理搭建本地 yum 服务器

    在政府,医院等单位有网络安全要求,对内外网进行物理隔离,然而内网主机无法访问互联网下载安装包,通过Nginx 反向代理搭建本地yum服务器实现内网主机安装包下载. Centos 8.2 部署 Ngin ...

  2. java基础二、类与继承

    员工类 Employee, 经理类:Manager public class Employee { private String name; private double salary; privat ...

  3. 驱动开发:通过Async反向与内核通信

    在前几篇文章中给大家具体解释了驱动与应用层之间正向通信的一些经典案例,本章将继续学习驱动通信,不过这次我们学习的是通过运用Async异步模式实现的反向通信,反向通信机制在开发中时常被用到,例如一个杀毒 ...

  4. css自定义会话框

    效果图图下: HTML代码: <div style="background-color: transparent; border: 1px #DDDDDD solid; width: ...

  5. POJ1985 Cow Marathon (树的直径)

    用两次dfs求出树的直径,这两次dfs可以写在一起,当然为了方便理解,这里是分开写的. 1 //两次dfs求树的重心 2 #include<cstdio> 3 #include<cs ...

  6. n维偏序 方法记录

    题解 首先我们要对一个地点能否到达建立认知:一个地点能到达不仅仅是能从它的上一个点或上上个点跳到,而是能从第一个点开始跳一路跳到.就好比说,咱吃了6个包子吃饱了,但咱不能只付第6个包子的钱. 方法一: ...

  7. 大数据技术之HBase原理与实战归纳分享-上

    @ 目录 概述 定义 特点 数据模型 概述 逻辑结构 物理存储结构 数据模型 应用场景 基础架构 安装 前置条件 部署 启动服务 高可用 Shell操作 基础操作 命令空间 DDL DML 概述 定义 ...

  8. vivo互联网机器学习平台的建设与实践

    vivo 互联网产品团队 - Wang xiao 随着广告和内容等推荐场景的扩展,算法模型也在不断演进迭代中.业务的不断增长,模型的训练.产出迫切需要进行平台化管理.vivo互联网机器学习平台主要业务 ...

  9. CEOI2020 道路(Roads) Solution

    直接来构造. 考虑扫描线.从左到右扫,考虑当前扫到了一个左端点,我们把这个左端点连到其他点上. 我们可以找到这个点下方离他最近的线段,并且记下每条线段上方在扫描线左侧且最靠右,与这条线段中间没有其他线 ...

  10. 图解 | 聊聊 MyBatis 缓存

    首发公众号-悟空聊架构:图解 | 聊聊 MyBatis 缓存 你好,我是悟空. 本文主要内容如下: 一.MyBatis 缓存中的常用概念 MyBatis 缓存:它用来优化 SQL 数据库查询的,但是可 ...