在使用分层或者分模块化的项目中,我们可能定义各种各样的O,例如:DO,VO,DTO等等。我们在进行这些对象之间的拷贝时,通过手动写get/set方法进行属性之间的赋值。因为他们之间的属性大部分都是相同的,不仅浪费时间,并且还有大量重复代码。所以,各种框架都添加的对象之间的拷贝的工具类。例如:

  1. Spring自带了BeanUtils

  2. Apatch自带的BeanUtils

  3. Apatch自带的PropertyUtils

  4. mapstruct提供Mappers

本文主要介绍MapStruct的基础知识、Mapstruct的优缺点、Mapctruct的拷贝示例、以及四种方法时间对比。

01

Mapstuct介绍


1.mapstruct的官网地址:

https://mapstruct.org

2.mapstruct的文档地址:

https://mapstruct.org/documentation/stable/reference/html/

3.mapstrcut的示例github地址

https://github.com/mapstruct/mapstruct-examples

4.mapstruct的介绍

mapstruct的作用:就像mapstruct官网所说的:mapsrtuct是一个用于简化在JavaBean之间映射的代码生成器工具,并且是基于约定高于配置的方式。

因为生成的代码使用简单的get/set方法,所以mapstruct可以更快执行、类型之间复制更安全、且容易理解。

02

mapstruct的优缺点


下面看看mapstruct相比其他映射工具有什么优点

1.mapstruct的优点:

  • mapstruct是在代码编译的时候,生成其映射规则的代码,所以会在编译时暴露映射错误的代码,让错误提前暴露。

  • 因为使用的是一些简单方法:set/get或者Fluent方式,而非反射方式,所以可以更快的执行完成。可以通过04章节查看各个方法效率上对比

  • 可以实现深拷贝,上面4种方法中只有mapstruct可以实现深拷贝。但是需要进行配置(使用@mapper(mappingControl=DeepClone.class)进行设置)。其他的都是浅拷贝。从而mapstruct实现修改新对象不会对老对象产生影响。

  • 类型更加安全

  • 可以进行自定义的映射。可以自定义各种方法,完成属性映射。例如:将两个属性数据合并成一个设置到目标属性上

2.mapstruct的缺点

  • 必须添加一个接口或者抽象类,才能实现映射。其他的三种方法都不用写额外接口或者类。

03

Mapstruct的简单示例使用


上面主要是介绍一下Mapstruct的基础知识,下面看一个mapstruct的简单示例,完成属性之间的映射。

在示例中,我们使用mapstruct的1.4.2.Final版本,进行试验的

1.第一个原属性数据

//第一个拷贝对象
public class CompareA {private String name;private Integer age;private LocalDateTime createTime;private double score;private Double totalScore;private Date updateTIme;private ChildCompare childCompare;private Long days;//省略其get/set方法,也可使用Lombok,以后讲解lombok与mapstruct结合使用}

2. 第二个目标属性数据

public class CompareB {private String name;private Integer age;private String createTime;private double score;private Double totalScore;private Date updateTIme;private ChildCompare childCompare;private Long day;//省略其get/set方法,
}

3.子类

//这是CompareA和CompareB共有的对象
public class ChildCompare {private String childName;private long childAge;//省略其get/set方法,
}

上面是两个实体对象,观察这两个实体可以看到有两点不同:

  • CompareA中有一个createTime属性,数据类型为LocalDateTime,CompareB也有一个createTime,但是属性类型为String
  • CompareA有一个days属性,而CompareB有一个day属性

4.mapstruct接口(这是需要我们手动写的)

package com.moxiao.properties.mapstruct;import com.moxiao.properties.entity.CompareA;
import com.moxiao.properties.entity.CompareB;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.control.DeepClone;
import org.mapstruct.factory.Mappers;/*** @author moxiao*/
@Mapper
public interface CompareMapper {CompareMapper INSTANCE = Mappers.getMapper(CompareMapper.class);@Mapping(target = "day", source = "days")@Mapping(target = "createTime", source = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")@Mapping(target = "childCompare", source = "childCompare", mappingControl = DeepClone.class)CompareB mapper(CompareA compareA);
}

从上面的实现中,我们加入了三个注解:

  • 第一个注解:完成属性名不匹配的问题,将compareA中的days(source)赋值到compareB的day(target)
  • 第二个注解:完成属性类型不匹配的问题,将compareA中的createTime,赋值到compareB的createTime,这里进行日期的格式转化,使用dateFormat设置格式化类型
  • 第三个注解:完成深度拷贝,将compareA中的childCompare属性进行深度拷贝,而不是指针复制,最重要的是最后一个参数:mappingControl = DeepClone.class

5.最后就会自动生成一个实现类:

package com.moxiao.properties.mapstruct;import com.moxiao.properties.entity.ChildCompare;
import com.moxiao.properties.entity.CompareA;
import com.moxiao.properties.entity.CompareB;
import java.time.format.DateTimeFormatter;
import javax.annotation.Generated;@Generated(value = "org.mapstruct.ap.MappingProcessor"
)
public class CompareMapperImpl implements CompareMapper {@Overridepublic CompareB mapper(CompareA compareA) {if ( compareA == null ) {return null;}CompareB compareB = new CompareB();compareB.setDay( compareA.getDays() );if ( compareA.getCreateTime() != null ) {compareB.setCreateTime( DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" ).format( compareA.getCreateTime() ) );}compareB.setChildCompare( childCompareToChildCompare( compareA.getChildCompare() ) );compareB.setName( compareA.getName() );compareB.setAge( compareA.getAge() );compareB.setScore( compareA.getScore() );compareB.setTotalScore( compareA.getTotalScore() );compareB.setUpdateTIme( compareA.getUpdateTIme() );return compareB;}protected ChildCompare childCompareToChildCompare(ChildCompare childCompare) {if ( childCompare == null ) {return null;}ChildCompare childCompare1 = new ChildCompare();childCompare1.setChildName( childCompare.getChildName() );childCompare1.setChildAge( childCompare.getChildAge() );return childCompare1;}
}

从上面的实现,我们可以看出,添加一个childCompareToChildCompare方法,来完成对象之间的深度拷贝。同样的将days设置到了day属性,等等。

6.最后,我们调用这个mapper方法,就可以完成对象之间的拷贝:如下:

CompareMapper.INSTANCE.mapper(new CompareA());

04

这个对象映射之间的比较


我们在1,000,000个对象进行各个工具之间的对比,看看效率如何?

package com.moxiao.properties.mapstruct;import com.moxiao.properties.entity.ChildCompare;
import com.moxiao.properties.entity.CompareA;
import com.moxiao.properties.entity.CompareB;
import org.apache.commons.beanutils.PropertyUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StopWatch;import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.Date;/*** @author moxiao*/
public class CompareTest {int count = 1000000;/*** Spring中的BeanUtils的使用,最后使用了6700ms*/@Testpublic void springBeanUtils() {CompareA populate = populate();StopWatch stopWatch = new StopWatch();stopWatch.start("开始拷贝");for (int i = 0; i < count; i++) {CompareB compareB = new CompareB();BeanUtils.copyProperties(populate, compareB);}stopWatch.stop();System.out.println(stopWatch.getTotalTimeMillis());}/*** 这是使用mapstruct进行对象拷贝,使用时间54ms*/@Testpublic void mapstructBeanUtils() {CompareA populate = populate();StopWatch stopWatch = new StopWatch();stopWatch.start("开始拷贝");for (int i = 0; i < count; i++) {CompareB mapper = CompareMapper.INSTANCE.mapper(populate);}stopWatch.stop();System.out.println(stopWatch.getTotalTimeMillis());}/*** 这是Apache的BeanUtils工具,使用7086ms* @throws InvocationTargetException* @throws IllegalAccessException*/@Testpublic void commonBeanUtils() throws InvocationTargetException, IllegalAccessException {CompareA populate = populate();StopWatch stopWatch = new StopWatch();stopWatch.start("开始拷贝");for (int i = 0; i < count; i++) {CompareB compareB = new CompareB();org.apache.commons.beanutils.BeanUtils.copyProperties(compareB, populate);}stopWatch.stop();System.out.println(stopWatch.getTotalTimeMillis());}/*** 使用Apache中的propertyUtils静态方法,使用3756ms* @throws IllegalAccessException* @throws NoSuchMethodException* @throws InvocationTargetException*/@Testpublic void commonPropertiesUtils() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {CompareA populate = populate();StopWatch stopWatch = new StopWatch();stopWatch.start("开始拷贝");for (int i = 0; i < count; i++) {CompareB compareB = new CompareB();PropertyUtils.copyProperties(compareB, populate);}stopWatch.stop();System.out.println(stopWatch.getTotalTimeMillis());}public CompareA populate() {CompareA compareA = new CompareA();compareA.setAge(10);compareA.setName("moxiao");compareA.setCreateTime(LocalDateTime.now());compareA.setDays(100L);compareA.setTotalScore(100.0);compareA.setScore(12.3);compareA.setUpdateTIme(new Date());ChildCompare childCompare = new ChildCompare();childCompare.setChildAge(200);childCompare.setChildName("moxiaoChild");compareA.setChildCompare(childCompare);return compareA;}}

pom文件如下:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.moxiao</groupId><artifactId>propertiescopytest</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.9</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.3.9</version></dependency><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>1.4.2.Final</version></dependency><!-- lombok dependencies should not end up on classpath --><!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.18</version><scope>provided</scope></dependency><dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.4</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.projectlombok</groupId><artifactId>lombok-mapstruct-binding</artifactId><version>0.2.0</version></path><path><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.4.2.Final</version></path><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.18</version></path></annotationProcessorPaths><showWarnings>true</showWarnings><compilerArgs><arg>-Amapstruct.suppressGeneratorTimestamp=true</arg><arg>-Amapstruct.suppressGeneratorVersionInfoComment=true</arg></compilerArgs></configuration></plugin></plugins></build></project>

上面是各个工具之间的对比,1000000数据之间拷贝的时间为:

工具名称 时间(ms)
mapstruct 54
Spring的BeanUtils 6700
Apache的common的BeanUtils 7086
Apache的common的PropertyUtils 3756

综上所述,1百万数据,mapstruct可以在50ms左右完成。

这篇文章只是Mapstruct文章的开头,后续还有:

  • 与Lombok结合使用以及注意事项。
  • 与SpringBoot进行DI
  • 对象之间的深度拷贝,需要最什么配置

更多精彩内容:请关注公众号:


Mapstruct使用说明(一)相关推荐

  1. abaqus高性能服务器怎么用,高性能计算平台ABAQUS任务调度使用说明作者陈林E-Mailchenlin.PDF...

    高性能计算平台ABAQUS任务调度使用说明作者陈林E-Mailchenlin.PDF 高性能计算平台ABAQUS 任务调度使用说明 作者:陈林 E-Mail:chenlin@ 日期:2017-1-10 ...

  2. linux 文件拷贝并替换,Linux_cmd replace 文件替换使用说明,帮助信息: 复制代码 代码如 - phpStudy...

    cmd replace 文件替换使用说明 帮助信息: 复制代码 代码如下: 替换文件. REPLACE [drive1:][path1]filename [drive2:][path2] [/A] [ ...

  3. Simple Dynamic Strings(SDS)源码解析和使用说明二

    在<Simple Dynamic Strings(SDS)源码解析和使用说明一>文中,我们分析了SDS库中数据的基本结构和创建.释放等方法.本文将介绍其一些其他方法及实现.(转载请指明出于 ...

  4. Delphi开发的IOCP测试Demo以及使用说明。

    Delphi开发的IOCP,此为压力测试Demo和使用说明.

  5. 还在用 BeanUtils来做对象转换吗?快试试 MapStruct吧

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:阿进的写字台 https://www.cnblogs ...

  6. 丢弃掉那些BeanUtils工具类吧,MapStruct真香!!!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 在前几天的文章<为什么阿里巴巴禁止使用Apache Bean ...

  7. oracle database link mysql_oracle database link使用说明

    oracle database link使用说明 作用: 将多个oracle数据库逻辑上看成一个数据库,也就是说在一个数据库中可以操作另一个数据库中的对象. 简易语法: CREATE [PUBLIC] ...

  8. 序列拼接工具Bowtie使用说明

    序列拼接工具Bowtie使用说明 2011-06-08 ~ ADMIN Bowtie是一个超级快速的,较为节省内存的短序列拼接至模板基因组的工具.它在拼接35碱基长度的序列时,可以达到每小时2.5亿次 ...

  9. benchmarksql测试mysql_数据库压力测试工具 -- BenchmarkSQL 使用说明

    关于数据库的压力测试,之前写过3篇Blog: 数据库基准测试(Database Benchmarking) 说明 数据库压力测试工具 -- Hammerdb 使用说明 数据库压力测试工具 -- Swi ...

最新文章

  1. 图论:关于二分图的总结(转载)
  2. mysql索引有哪些_MySQL索引是个什么东西
  3. Linux非阻塞IO(八)使用epoll重新实现非阻塞的回射服务器
  4. php 删除硬链接,为什么要用软链接硬链接
  5. CodeForces - 1364C Ehab and Prefix MEX(贪心+构造)
  6. mysql用binlog恢复数据_利用mysql的binlog恢复数据
  7. flex 瀑布流 (多列样式column布局内容被截断)
  8. android文件添加一行代码怎么写,Android:以编程方式添加Textview,而不是将文本包装到下一行(示例代码)...
  9. iPhone 9测试工作结束:4.7英寸小屏设计 主打新兴市场
  10. CSS Sprites在IE中不起作用[8/7/6]
  11. android 按键上报流程,input子系统详解,按键如何上报
  12. sql格式化工具-SQL Pretty Printer
  13. 【多线程】如何保证线程安全
  14. MATLAB 级数计算
  15. css3怎么实现筛子的效果??transform得常用属性??
  16. 股票买卖原理_如何在智能手机上买卖股票
  17. iOS 通讯录-获取联系人属性
  18. 详解服务器异构计算FPGA基础知识
  19. 个人总结-网络安全学习和CTF必不可少的一些网站
  20. [嵌入式]汉字字模点阵

热门文章

  1. SpringBoot 修改 Multipart 文件大小
  2. 阿里P7架构师,带你点亮程序员蜕变之路
  3. 【数据增强】——弹性变形(Elastic Distortion)
  4. 【MCU】MDK错误:No Browse Information available in ‘..\OBJ\SPI‘
  5. KEIL MDK 提示“no browse info for symbol in this context”
  6. WebGoat之JWT部分攻略
  7. ISCC2021 MISC wp
  8. 积极达成:处理好情绪再处理问题
  9. 【五六七人口普查】我国省市两级人口婚姻状况和妇女生育状况
  10. 解决数码管残影问题,数码管总是会显示上一位数字的残影