还在用BeanUtils拷贝对象?MapStruct才是王者!【附源码】

前几天,远在北京的小伙伴在群里抛出了 “MapStruct”的概念。对于只闻其名,未见其人的我来说,决定对其研究一番。本文我们就从 MapStruct 的概念出发,通过具体的代码示例来研究它的使用情况,最后与”市面上”的其它工具来做个对比!

官方介绍

首先我们打开 MapStruct官网地址,映入眼帘的就是下边的三步曲:

What is it?

MapStruct 是一个 代码生成器,它基于约定优先于配置的方法大大简化了 JavaBean 类型之间映射的实现。生成的映射代码使用 普通方法调用,因此速度快、类型 安全且易于理解。

Why?

多层应用程序通常需要在不同的对象模型(例如实体和 DTO)之间进行 映射。编写这样的映射代码是一项乏味且容易出错的任务。 MapStruct 旨在通过尽可能自动化来简化这项工作。

与其他映射框架不同, MapStruct编译时生成 bean 映射,这确保了高性能,允许快速的开发人员反馈和彻底的错误检查。

How?

MapStruct 是插入 Java 编译器的 注释处理器,可以在命令行构建( MavenGradle等)中使用,也可以在首选 IDE 中使用。它使用合理的默认值,但在配置或实现特殊行为时,用户可以自定义实现。

官网的解释总是咬文嚼字,晦涩难懂的,看到这你只需要记住 MapStruct 是用来做实体类映射——实体类拷贝 的就可以了。

源码地址:https://github.com/mapstruct/mapstruct
官网推荐的 Demo: https://github.com/mapstruct/mapstruct-examples

简单实现

我们注意到官网中有涉及到简单样例的实现,我们用2分钟来分析一波:

1. 引入依赖


    org.mapstruct
    mapstruct-jdk8
    1.3.0.Final

//注解处理器,根据注解自动生成mapper的实现

    org.mapstruct
    mapstruct-processor
    1.2.0.Final

我们在编译时会报 java: No property named "numberOfSeats" exists in source parameter(s). Did you mean "null"? 错误,经过查阅资料发现 mapstruct-processorLombok 的版本需要统一一下: mapstruct-processor1.2.0.FinalLombok1.16.14

2. 准备实体类 Car.java 和 数据传输类 CarDto.java

<br><br><br><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-class"><span class="hljs-keyword">class</span>&#xA0;<span class="hljs-title">Car</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;String&#xA0;make;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;<span class="hljs-keyword">int</span>&#xA0;numberOfSeats;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;CarType&#xA0;type;<br>}<br><br><br><br><br><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-class"><span class="hljs-keyword">class</span>&#xA0;<span class="hljs-title">CarDto</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;String&#xA0;make;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;<span class="hljs-keyword">int</span>&#xA0;seatCount;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;String&#xA0;type;<br><br>}

3. 创建映射器接口,里边定义映射方法

<br><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-class"><span class="hljs-keyword">interface</span>&#xA0;<span class="hljs-title">CarMapper</span>&#xA0;</span>{<br>&#xA0;<br>&#xA0;&#xA0;&#xA0;&#xA0;CarMapper&#xA0;INSTANCE&#xA0;=&#xA0;Mappers.getMapper(&#xA0;CarMapper<span class="hljs-class">.<span class="hljs-keyword">class</span>&#xA0;)</span>;<br><br>&#xA0;&#xA0;&#xA0;&#xA0;(source&#xA0;=&#xA0;<span class="hljs-string">"numberOfSeats"</span>,&#xA0;target&#xA0;=&#xA0;<span class="hljs-string">"seatCount"</span>)<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function">CarDto&#xA0;<span class="hljs-title">carToCarDto</span><span class="hljs-params">(Car&#xA0;car)</span></span>;&#xA0;<br>&#xA0;&#xA0;&#xA0;<br>}

解析分析:

  • @Mapper 将接口标记为映射接口,并允许 MapStruct 处理器在编译期间启动。这里的 @Mapper 注解不是 mybatis 的注解,而是 org.mapstruct.Mapper 的;
  • 实际映射方法 carToCarDto() 期望源对象 Car 作为参数,并返回目标对象 CarDto ,方法名可以自由选择;
  • 对于源对象和目标对象中具有 不同名称 的属性,可以使用 @Mapping 注释来配置名称;
  • 对于源对象和目标对象中具有 不同类型 的属性,也可以使用 @Mapping 注释来进行转换,比如:类型属性将从枚举类型转换为字符串;
  • 一个接口中可以有多个映射方法,对于所有的这些方法, MapStruct 将生成一个实现;
  • 该接口的实现实例可以从 Mappers 中获得,接口声明一个 INSTANCE ,为客户端提供对映射器实现的访问。

4. 实现类

还在用BeanUtils拷贝对象?MapStruct才是王者!【附源码】

从代码中可以看出 MapStruct 为我们自动生成了 set/get 代码,并且对 枚举类进行了特殊处理。

5. 客户端

<br><span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">shouldMapCarToDto</span><span class="hljs-params">()</span>&#xA0;</span>{<br><br>&#xA0;&#xA0;&#xA0;&#xA0;Car&#xA0;car&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;Car(&#xA0;<span class="hljs-string">"Morris"</span>,&#xA0;<span class="hljs-number">5</span>,&#xA0;CarType.SEDAN&#xA0;);<br>&#xA0;&#xA0;&#xA0;&#xA0;CarDto&#xA0;carDto&#xA0;=&#xA0;CarMapper.INSTANCE.carToCarDto(&#xA0;car&#xA0;);<br>&#xA0;&#xA0;&#xA0;&#xA0;System.out.println(carDto);<br>&#xA0;&#xA0;&#xA0;&#xA0;<br>}

还在用BeanUtils拷贝对象?MapStruct才是王者!【附源码】

小结: MapStruct 基于 mapper 接口,在 编译期动态生成 set/get 代码的 class 文件 ,在运行时直接调用该 class 文件。

MapStruct 配置

@Mapper

我们翻开上边提到的 Mapper 注释的源码,该注释的解释是:将接口或抽象类标记为 映射器,并通过 MapStruct 激活 该类型实现的生成。我们找到其中的 componentModel 属性,默认值为 default,它有四种值供我们选择:

  • default:映射器不使用组件模型,实例通常通过 Mappers.getMapper&#xFF08;java.lang.Class&#xFF09; 获取;
  • cdi:生成的映射器是 application-scopedCDI bean ,可以通过 @Inject 获取;
  • spring:生成的映射器是 Spring bean ,可以通过 @Autowired 获取;
  • jsr330:生成的映射器被 @javax.inject.Named@Singleton 注释,可以通过 @inject 获取;

上边我们用的就是默认的方法,当然我们也可以用 @Autowired 来引入接口依赖,此处不再举例,有兴趣的小伙伴可以自己试试!

另外我们可以看下 uses 属性:可以通过定义其他类来完成字段转换,接下来我们来个小例子演示一下:

1. 定义一个 CarVo.java 类

<br><br><br><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-class"><span class="hljs-keyword">class</span>&#xA0;<span class="hljs-title">CarVo</span>&#xA0;</span>{<br><br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;String&#xA0;make;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;<span class="hljs-keyword">int</span>&#xA0;seatCount;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;<span class="hljs-keyword">boolean</span>&#xA0;type;<br>}

2. 在 mapper 中定义一个 vo 转为 dto 的方法 CarDto carVoToCarDto(CarVo carVo);

当不加 uses 属性时,查看编译后生成的实现类

<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;CarDto&#xA0;<span class="hljs-title">carVoToCarDto</span><span class="hljs-params">(CarVo&#xA0;carVo)</span>&#xA0;</span>{<br>&#xA0;<span class="hljs-keyword">if</span>&#xA0;(carVo&#xA0;==&#xA0;<span class="hljs-keyword">null</span>)&#xA0;{<br>&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;<span class="hljs-keyword">null</span>;<br>&#xA0;}&#xA0;<span class="hljs-keyword">else</span>&#xA0;{<br>&#xA0;&#xA0;CarDto&#xA0;carDto&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;CarDto();<br>&#xA0;&#xA0;carDto.setMake(carVo.getMake());<br>&#xA0;&#xA0;carDto.setSeatCount(carVo.getSeatCount());<br>&#xA0;&#xA0;carDto.setType(String.valueOf(carVo.isType()));<br>&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;carDto;<br>&#xA0;}<br>}
  1. mapper 上增加 uses 属性,并指定自定义的处理类,代码如下:
(uses&#xA0;=&#xA0;{BooleanStrFormat<span class="hljs-class">.<span class="hljs-keyword">class</span>})<br><span class="hljs-title">public</span>&#xA0;<span class="hljs-title">interface</span>&#xA0;<span class="hljs-title">CarMapper</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;......<br>}<br><br><br><br><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-class"><span class="hljs-keyword">class</span>&#xA0;<span class="hljs-title">BooleanStrFormat</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;String&#xA0;<span class="hljs-title">toStr</span><span class="hljs-params">(<span class="hljs-keyword">boolean</span>&#xA0;type)</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">if</span>(type){<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;<span class="hljs-string">"Y"</span>;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;}<span class="hljs-keyword">else</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;<span class="hljs-string">"N"</span>;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;}<br><br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">boolean</span>&#xA0;<span class="hljs-title">toBoolean</span><span class="hljs-params">(String&#xA0;type)</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">if</span>&#xA0;(type.equals(<span class="hljs-string">"Y"</span>))&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;<span class="hljs-keyword">true</span>;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;}&#xA0;<span class="hljs-keyword">else</span>&#xA0;{<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;<span class="hljs-keyword">false</span>;<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;}<br>&#xA0;&#xA0;&#xA0;&#xA0;}<br>}<br><br><br><span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;CarDto&#xA0;<span class="hljs-title">carVoToCarDto</span><span class="hljs-params">(CarVo&#xA0;carVo)</span>&#xA0;</span>{<br>&#xA0;<span class="hljs-keyword">if</span>&#xA0;(carVo&#xA0;==&#xA0;<span class="hljs-keyword">null</span>)&#xA0;{<br>&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;<span class="hljs-keyword">null</span>;<br>&#xA0;}&#xA0;<span class="hljs-keyword">else</span>&#xA0;{<br>&#xA0;&#xA0;CarDto&#xA0;carDto&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;CarDto();<br>&#xA0;&#xA0;carDto.setMake(carVo.getMake());<br>&#xA0;&#xA0;carDto.setSeatCount(carVo.getSeatCount());<br>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;<br>&#xA0;&#xA0;carDto.setType(<span class="hljs-keyword">this</span>.booleanStrFormat.toStr(carVo.isType()));<br>&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;carDto;<br>&#xA0;}<br>}

4.客户端代码

<br><span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">shouldMapCarVoToDto</span><span class="hljs-params">()</span>&#xA0;</span>{<br><br>&#xA0;CarVo&#xA0;carVo&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;CarVo(&#xA0;<span class="hljs-string">"Morris"</span>,&#xA0;<span class="hljs-number">5</span>,&#xA0;<span class="hljs-keyword">false</span>&#xA0;);<br>&#xA0;CarDto&#xA0;carDto&#xA0;=&#xA0;CarMapper.INSTANCE.carVoToCarDto(&#xA0;carVo&#xA0;);<br><br>&#xA0;System.out.println(carDto);<br>}

还在用BeanUtils拷贝对象?MapStruct才是王者!【附源码】

@Mapping

@Mapping 可以用来配置一个 bean 属性或枚举常量的映射,默认是将具有相同名称的属性进行映射,当然也可以用 sourceexpression 或者 constant 属性手动指定,接下来我们来分析下常用的属性值。

  1. target:属性的目标名称,同一目标属性不能映射多次。如果用于映射枚举常量,则将给出常量成员的名称,在这种情况下,源枚举中的多个值可以映射到目标枚举的相同值。
  2. source:属性的源名称,

  3. 如果带注释的方法有多个源参数,则属性名称必须使用参数名称限定,例如 &#x201C;addressParam.city"

  4. 当找不到匹配的属性时, MapStruct 将查找匹配的参数名称;
  5. 当用于映射枚举常量时,将给出常量成员的名称;
  6. 该属性不能与 constantexpression 一起使用;

  7. dateFormat:通过 SimpleDateFormat 实现 StringDate 日期之间相互转换。

  8. numberFormat:通过 DecimalFormat 实现 NumberString 的数值格式化。
  9. constant:设置指定目标属性的常量字符串,当指定的目标属性的类型为: primitiveboxed (例如 Long )时, MapStruct 检查是否可以将该 primitive 作为有效的文本分配给 primitiveboxed 类型。如果可能, MapStruct 将分配为文字;如果不可能, MapStruct 将尝试应用用户定义的映射方法。 另外, MapStruct 将常量作为字符串处理,将通过应用匹配方法、类型转换方法或内置转换来转换该值。此属性不能与 sourcedefaultValuedefaultExpressionexpression 一起使用。
  10. expression:是一个表达式,根据该表达式设置指定的目标属性。他的属性不能与 sourcedefaultValuedefaultExpressionconstant 一起使用。
  11. ignore: 忽略这个字段。

我们用 expression 这个属性来实现一下上边用 uses 实现的案例:

1. 在 mapper 中定义方法

(target&#xA0;=&#xA0;<span class="hljs-string">"type"</span>,&#xA0;expression&#xA0;=&#xA0;<span class="hljs-string">"java(new&#xA0;com.ittest.controller.BooleanStrFormat().toStr(carVo.isType()))"</span>)<br><span class="hljs-function">CarDto&#xA0;<span class="hljs-title">carVoToDtoWithExpression</span><span class="hljs-params">(CarVo&#xA0;carVo)</span></span>;

2. 生成的实现类

<br><span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;CarDto&#xA0;<span class="hljs-title">carVoToDtoWithExpression</span><span class="hljs-params">(CarVo&#xA0;carVo)</span>&#xA0;</span>{<br>&#xA0;<span class="hljs-keyword">if</span>&#xA0;(&#xA0;carVo&#xA0;==&#xA0;<span class="hljs-keyword">null</span>&#xA0;)&#xA0;{<br>&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;<span class="hljs-keyword">null</span>;<br>&#xA0;}<br><br>&#xA0;CarDto&#xA0;carDto&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;CarDto();<br><br>&#xA0;carDto.setMake(&#xA0;carVo.getMake()&#xA0;);<br>&#xA0;carDto.setSeatCount(&#xA0;carVo.getSeatCount()&#xA0;);<br><br>&#xA0;carDto.setType(&#xA0;<span class="hljs-keyword">new</span>&#xA0;com.ittest.controller.BooleanStrFormat().toStr(carVo.isType())&#xA0;);<br><br>&#xA0;<span class="hljs-keyword">return</span>&#xA0;carDto;<br>}

3. 客户端

<br><span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">mapCarVoToDtoWithExpression</span><span class="hljs-params">()</span>&#xA0;</span>{<br><br>&#xA0;CarVo&#xA0;carVo&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;CarVo(&#xA0;<span class="hljs-string">"Morris"</span>,&#xA0;<span class="hljs-number">5</span>,&#xA0;<span class="hljs-keyword">false</span>&#xA0;);<br>&#xA0;CarDto&#xA0;carDto&#xA0;=&#xA0;CarMapper.INSTANCE.carVoToDtoWithExpression(&#xA0;carVo&#xA0;);<br><br>&#xA0;System.out.println(carDto);<br>}

还在用BeanUtils拷贝对象?MapStruct才是王者!【附源码】

至于其他的用法大家可以多多探索。

重要提示:枚举映射功能已被弃用,并被 ValueMapping 取代。它将在后续版本中删除。

@Mappings

可以配置多个 @Mapping,例如

({<br>&#xA0;&#xA0;&#xA0;&#xA0;(source&#xA0;=&#xA0;<span class="hljs-string">"id"</span>,&#xA0;target&#xA0;=&#xA0;<span class="hljs-string">"carId"</span>),<br>&#xA0;&#xA0;&#xA0;&#xA0;(source&#xA0;=&#xA0;<span class="hljs-string">"name"</span>,&#xA0;target&#xA0;=&#xA0;<span class="hljs-string">"carName"</span>),<br>&#xA0;&#xA0;&#xA0;&#xA0;(source&#xA0;=&#xA0;<span class="hljs-string">"color"</span>,&#xA0;target&#xA0;=&#xA0;<span class="hljs-string">"carColor"</span>)<br>})

@MappingTarget

用于更新已有对象,还是用例子来说明吧:

1. 创建 BMWCar.java 类

<br><br><br><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-class"><span class="hljs-keyword">class</span>&#xA0;<span class="hljs-title">BMWCar</span>&#xA0;</span>{<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;String&#xA0;make;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;<span class="hljs-keyword">int</span>&#xA0;numberOfSeats;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;CarType&#xA0;type;<br><br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;String&#xA0;color;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;String&#xA0;price;<br><br>}

2. mapper 中创建更新方法,并查看实现类

<br><span class="hljs-function"><span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">updateBwmCar</span><span class="hljs-params">(Car&#xA0;car,&#xA0;@MappingTarget&#xA0;BMWCar&#xA0;bwmCar)</span></span>;<br><br><br><span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">updateBwmCar</span><span class="hljs-params">(Car&#xA0;car,&#xA0;BMWCar&#xA0;bwmCar)</span>&#xA0;</span>{<br>&#xA0;<span class="hljs-keyword">if</span>&#xA0;(car&#xA0;!=&#xA0;<span class="hljs-keyword">null</span>)&#xA0;{<br>&#xA0;&#xA0;bwmCar.setMake(car.getMake());<br>&#xA0;&#xA0;bwmCar.setNumberOfSeats(car.getNumberOfSeats());<br>&#xA0;&#xA0;bwmCar.setType(car.getType());<br>&#xA0;}<br>}

3. 客户端代码

<br><span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">updateBwmCar</span><span class="hljs-params">()</span>&#xA0;</span>{<br>&#xA0;Car&#xA0;car&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;Car(&#xA0;<span class="hljs-string">"Morris"</span>,&#xA0;<span class="hljs-number">5</span>,&#xA0;CarType.SEDAN&#xA0;);<br>&#xA0;BMWCar&#xA0;bwmCar&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;BMWCar(<span class="hljs-string">"BWM"</span>,&#xA0;<span class="hljs-number">5</span>,&#xA0;CarType.SPORTS,&#xA0;<span class="hljs-string">"RED"</span>,&#xA0;<span class="hljs-string">"50w"</span>);<br>&#xA0;System.out.println(<span class="hljs-string">"&#x66F4;&#x65B0;&#x524D;&#xA0;car:"</span>+car.toString());<br>&#xA0;System.out.println(<span class="hljs-string">"&#x66F4;&#x65B0;&#x524D;&#xA0;BWMCar:"</span>+bwmCar.toString());<br><br>&#xA0;CarMapper.INSTANCE.updateBwmCar(car,&#xA0;bwmCar);<br><br>&#xA0;System.out.println(<span class="hljs-string">"&#x66F4;&#x65B0;&#x540E;&#xA0;car:"</span>+car.toString());<br>&#xA0;System.out.println(<span class="hljs-string">"&#x66F4;&#x65B0;&#x540E;&#xA0;BWMCar:"</span>+bwmCar.toString());<br>}

还在用BeanUtils拷贝对象?MapStruct才是王者!【附源码】

扩展:多个对象映射一个对象

1. 准备实体类 Benz4SMall.javaMall4S.java

<br><br><br><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-class"><span class="hljs-keyword">class</span>&#xA0;<span class="hljs-title">Mall4S</span>&#xA0;</span>{<br><br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;String&#xA0;address;<br><br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;String&#xA0;mobile;<br><br>}<br><br><br><br><br><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-class"><span class="hljs-keyword">class</span>&#xA0;<span class="hljs-title">Benz4SMall</span>&#xA0;</span>{<br><br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;String&#xA0;address;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;String&#xA0;mobile;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;String&#xA0;make;<br>&#xA0;&#xA0;&#xA0;&#xA0;<span class="hljs-keyword">private</span>&#xA0;<span class="hljs-keyword">int</span>&#xA0;numberOfSeats;<br>}

2. mapper 创建转换方法并查看生成的实现类

<span class="hljs-function">Benz4SMall&#xA0;<span class="hljs-title">mallCarToBenzMall</span><span class="hljs-params">(Car&#xA0;car,&#xA0;Mall4S&#xA0;mall4S)</span></span>;<br><br><br><span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;Benz4SMall&#xA0;<span class="hljs-title">mallCarToBenzMall</span><span class="hljs-params">(Car&#xA0;car,&#xA0;Mall4S&#xA0;mall4S)</span>&#xA0;</span>{<br>&#xA0;<span class="hljs-keyword">if</span>&#xA0;(car&#xA0;==&#xA0;<span class="hljs-keyword">null</span>&#xA0;&&&#xA0;mall4S&#xA0;==&#xA0;<span class="hljs-keyword">null</span>)&#xA0;{<br>&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;<span class="hljs-keyword">null</span>;<br>&#xA0;}&#xA0;<span class="hljs-keyword">else</span>&#xA0;{<br>&#xA0;&#xA0;Benz4SMall&#xA0;benz4SMall&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;Benz4SMall();<br>&#xA0;&#xA0;<span class="hljs-keyword">if</span>&#xA0;(car&#xA0;!=&#xA0;<span class="hljs-keyword">null</span>)&#xA0;{<br>&#xA0;&#xA0;&#xA0;benz4SMall.setMake(car.getMake());<br>&#xA0;&#xA0;&#xA0;benz4SMall.setNumberOfSeats(car.getNumberOfSeats());<br>&#xA0;&#xA0;}<br><br>&#xA0;&#xA0;<span class="hljs-keyword">if</span>&#xA0;(mall4S&#xA0;!=&#xA0;<span class="hljs-keyword">null</span>)&#xA0;{<br>&#xA0;&#xA0;&#xA0;benz4SMall.setAddress(mall4S.getAddress());<br>&#xA0;&#xA0;&#xA0;benz4SMall.setMobile(mall4S.getMobile());<br>&#xA0;&#xA0;}<br><br>&#xA0;&#xA0;<span class="hljs-keyword">return</span>&#xA0;benz4SMall;<br>&#xA0;}<br>}<br>

3. 客户端

<br><span class="hljs-function"><span class="hljs-keyword">public</span>&#xA0;<span class="hljs-keyword">void</span>&#xA0;<span class="hljs-title">mallCarToBenzMall</span><span class="hljs-params">()</span>&#xA0;</span>{<br>&#xA0;Car&#xA0;car&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;Car(&#xA0;<span class="hljs-string">"Morris"</span>,&#xA0;<span class="hljs-number">5</span>,&#xA0;CarType.SEDAN&#xA0;);<br>&#xA0;Mall4S&#xA0;mall4S&#xA0;=&#xA0;<span class="hljs-keyword">new</span>&#xA0;Mall4S(<span class="hljs-string">"&#x5317;&#x4EAC;&#x5E02;"</span>,&#xA0;<span class="hljs-string">"135XXXX4503"</span>);<br>&#xA0;Benz4SMall&#xA0;benz4SMall&#xA0;=&#xA0;CarMapper.INSTANCE.mallCarToBenzMall(car,&#xA0;mall4S);<br>&#xA0;System.out.println(benz4SMall.toString());<br>}

还在用BeanUtils拷贝对象?MapStruct才是王者!【附源码】

深拷贝与浅拷贝

深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制 实体,而不是引用。

假设 B 复制了 A ,修改 A 的时候,看 B 是否发生变化:如果 B 跟着也变了,说明是浅拷贝, 拿人手短!(修改堆内存中的同一个值);如果 B 没有改变,说明是深拷贝, 自食其力!(修改堆内存中的不同的值)

MapStruct 中是 创建新的对象,也就是 深拷贝

MapStruct 与其他 Copy 的对比

我们在平时的项目中经常会使用到拷贝的功能,今天我们就将他们做一下对比,直接抛出 ZhaoYingChao88 大佬的实验结果:

还在用BeanUtils拷贝对象?MapStruct才是王者!【附源码】

输出结果: &#x624B;&#x52A8;Copy >Mapstuct>= cglibCopy > springBeanUtils > apachePropertyUtils > apacheBeanUtils 可以理解为: &#x624B;&#x5DE5;&#x590D;&#x5236; > cglib > &#x53CD;&#x5C04; > Dozer

还在用BeanUtils拷贝对象?MapStruct才是王者!【附源码】

根据测试结果,我们可以得出在速度方面, MapStruct 是最好的,执行速度是 Apache BeanUtils 的10倍、 Spring BeanUtils 的 4-5倍、和 BeanCopier 的速度差不多。

总结:在大数据量级的情况下, MapStructBeanCopier 都有着较高的性能优势,其中 MapStruct 尤为优秀。如果你仅是在日常处理少量的对象时,选取哪个其实变得并不重要,但数据量大时建议还是使用 MapStructBeanCopier 的方式,提高接口性能。

参考链接:https://blog.csdn.net/ZYC88888/article/details/109681423?spm=1001.2014.3001.5501

回复”mapstruct”,即可获取源码呦!

以上就是今天的全部内容了,如果你有不同的意见或者更好的 idea,欢迎联系阿Q,添加阿Q可以加入技术交流群参与讨论呦!

Original: https://www.cnblogs.com/aqsaycode/p/15407009.html
Author: 阿Q说代码
Title: 还在用BeanUtils拷贝对象?MapStruct才是王者!【附源码】

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/685479/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 搬砖_kafka的一些命令

    有关kafka的一些命令 kafka查看当前消费组消费到哪个offset cd /opt/kafka/bin #&#x5373;&#x8FDB;&#x516…

    技术杂谈 2023年7月25日
    053
  • oracle查看版本

    查看oracle版本可以通过查询三个内置表来达到目的。 1.v$instance表,这个表可以查看实例和版本信息。 2.product_component_version表。 3….

    技术杂谈 2023年6月1日
    080
  • 在线英语词典

    https://dictionary.cambridge.org/dictionary/english-chinese-simplified/ Original: https://…

    技术杂谈 2023年6月1日
    086
  • Kettle_使用Pan.bat执行转换、Kitchen.bat执行作业

    注意:使用bat文件执行速度比执行在spoon.bat中执行慢很多 一、使用Pan.bat执行转换 Pan.bat文件路径:\pdi-ce-6.1.0.1-196\data-int…

    技术杂谈 2023年5月31日
    084
  • 无向图求所有路径C#版

    无向图求所有路径 using System; using System.Collections.Generic; using System.Linq; using System.T…

    技术杂谈 2023年5月31日
    095
  • Python 装饰器相关知识

    登录认证,用得比较多,还有各类日志。 模拟博客园登录,需求:在访问每个功能模块之前必须先验证是否已经登录,没有登录不让访问,如没有账号就注册一个再登录,三次登录不成功就退出整个程序…

    技术杂谈 2023年6月21日
    082
  • Go基础2:数据结构(一)

    这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记。 1.数组 数组是一段固定长度的连续内存区域。在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小…

    技术杂谈 2023年7月24日
    080
  • 《三十年来寻剑客》灵云志勤禅师

    《三十年来寻剑客》灵云志勤禅师 三十年来寻剑客,几回落叶又抽枝。自从一见桃花后,直至如今更不疑。 Original: https://www.cnblogs.com/LittleH…

    技术杂谈 2023年5月31日
    092
  • Flink 状态编程

    在Flink架构体系中,有状态计算可以说是Flink非常重要的特性之一 Flink优势: 支持高吞吐、低延迟、高性能 支持事件时间Event_time概念 支持有状态计算 有状态计…

    技术杂谈 2023年7月10日
    073
  • 「日志」Navicat统计的行数竟然和表实际行数不一致

    背景 近期为了保障线上数据库的稳定性,我决定针对一些大表的历史数据有计划地进行备份迁移,但是呢,发现一个奇特的现象,Navicat统计行数和表自身count统计数竟然不一致!?0….

    技术杂谈 2023年7月24日
    064
  • 我是仙人掌社论

    闲话被虎哥扬了,哼哼啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊! 遂水篇博客 . 我是仙人掌珂…

    技术杂谈 2023年7月23日
    093
  • seaborn学习笔记(五):绘制多子图

    3.1 map方法:指定绘图方法 ¶ PairGrid所有参数中,只有data是必传参数,实例化时,seaborn会根据传入数据集的各个字段情况,绘制n行n列个坐标系,但仅限于绘制…

    技术杂谈 2023年7月24日
    074
  • Netty 如何高效接收网络数据?一文聊透 ByteBuffer 动态自适应扩缩容机制

    本系列Netty源码解析文章基于 4.1.56.Final版本,公众号:bin的技术小屋,大家如果看到图片显示不了的话,可以查看公众号原文 前文回顾 在前边的系列文章中,我们从内核…

    技术杂谈 2023年7月11日
    085
  • PHP通过CURL获取远程文件header头信息

    使用CURL方法获取远程文件header头信息,与内置函数get_headers不同的是,这个方法不用完整下载文件,只是下载头部信息,速度理论会快一些。 Python reques…

    技术杂谈 2023年6月1日
    091
  • .netElasticSearch-Sql扩展类【原创】

    官方提供的是java sdk,并支持jdbc方式的查询结果输出;但是却没有.net sdk的支持。 开发 ElasticSearch-Sql 第三方开源项目的.net sdk,未来…

    技术杂谈 2023年7月24日
    067
  • Java8 日期时间API

    一、转换 1、转字符串 1.1、LocalDateTime与字符串 //LocalDateTime 转 字符串 String str = DateTimeFormatter.ofP…

    技术杂谈 2023年7月11日
    070
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球