属性复制神器-mapstruct
我们之前说到项目中会用到各种object,vo,bo,dto等等。我们需要在不同的对象上复制属性。
一、BeanUtils和PropertyUtils
我们最常用的就是Common包里面的BeanUtils,或者Spring里面的BeanUtils.
BeanUtils.copyProperties(dest, orig);
还有一个PropertyUtils
PropertyUtils.copyProperties(dest, orig);
它俩区别:
BeanUtils和PropertyUtils复制对象时,根据属性名进行复制。
如果属性名相同,但类型不同,BeanUtils会直接转换; 而PropertyUtils会直接抛出异常。
二、自己写转换Builder
上面的BeanUtils和PropertyUtils代码写起来很方便简洁,但是它的实现原理是基于反射,效率不是很高。如果是对服务响应要求很高的应用,用它们就不太合适。有一种方法,自己写转换关系,一个一个set。
public class PersonBuilder {
public static Person newPerson(Context context) {
Person person = new Person();
person.setDelRepeat(context.getDelRepeat());
person.setSendName(context.getSendName());
person.setSendType(context.getSendType());
person.setInputMdnList(context.getInputMdnList());
person.setSceneId(context.getSceneId());
person.setGroupIdList(context.getGroupIdList());
person.setContent(context.getContent());
person.setImportFile(context.getImportFile());
return person;
}
public static PersonVO newPersonVO(Context context) {
... ...
这种方法没有性能问题,但是效率不高,字段少的话工作量还行,如果字段很多,会写的很头疼
三、mapStruct
mapStruct的原理是基于第二种方法,但是它只用写一个接口和方法,会自动生成实现类,工作量和第一种相当,效率和第二种方法一样。

1、举个官方的例子
引入maven依赖包
...
<properties>
<org.mapstruct.version>1.4.0.Beta2</org.mapstruct.version>
</properties>
...
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies>
定义Car.java
public class Car {
private String make;
private int numberOfSeats;
private CarType type;
//constructor, getters, setters etc.
}
定义CarDto.java
public class CarDto {
private String make;
private int seatCount;
private String type;
//constructor, getters, setters etc.
}
定义mapper
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
@Mapping(source = "numberOfSeats", target = "seatCount")
CarDto carToCarDto(Car car);
}
使用mapper
@Test
public void shouldMapCarToDto() {
//given
Car car = new Car( "Morris", 5, CarType.SEDAN );
//when
CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );
//then
assertThat( carDto ).isNotNull();
assertThat( carDto.getMake() ).isEqualTo( "Morris" );
assertThat( carDto.getSeatCount() ).isEqualTo( 5 );
assertThat( carDto.getType() ).isEqualTo( "SEDAN" );
}
2、更新
mapStruct还可以更新已有的bean
@Mapper
public interface CarMapper {
void updateCarFromDto(CarDto carDto, @MappingTarget Car car);
}
3、支持数据类型转换
比如int转String
@Mapper
public interface CarMapper {
@Mapping(source = "price", numberFormat = "$#.00")
CarDto carToCarDto(Car car);
@IterableMapping(numberFormat = "$#.00")
List<String> prices(List<Integer> prices);
}
4、支持map,set,list
public interface SourceTargetMapper {
@MapMapping(valueDateFormat = "dd.MM.yyyy")
Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
}
@Mapper
public interface CarMapper {
Set<String> integerSetToStringSet(Set<Integer> integers);
List<CarDto> carsToCarDtos(List<Car> cars);
CarDto carToCarDto(Car car);
}
5、支持Stream
@Mapper
public interface CarMapper {
Set<String> integerStreamToStringSet(Stream<Integer> integers);
List<CarDto> carsToCarDtos(Stream<Car> cars);
CarDto carToCarDto(Car car);
}
6、自定义origin和dest的名称
@Mapper
public interface OrderMapper {
OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class );
@ValueMappings({
@ValueMapping(source = "EXTRA", target = "SPECIAL"),
@ValueMapping(source = "STANDARD", target = "DEFAULT"),
@ValueMapping(source = "NORMAL", target = "DEFAULT")
})
ExternalOrderType orderTypeToExternalOrderType(OrderType orderType);
}
7、支持自定义的工厂模式生成bean
用这种方式就不会new,而是用工厂的createXXX方法
public class DtoFactory {
public CarDto createCarDto() {
return // ... custom factory logic
}
}
public class EntityFactory {
public <T extends BaseEntity> T createEntity(@TargetType Class<T> entityClass) {
return // ... custom factory logic
}
}
@Mapper(uses= { DtoFactory.class, EntityFactory.class } )
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
CarDto carToCarDto(Car car);
Car carDtoToCar(CarDto carDto);
}
8、其它
还有其它的高级功能,自行到官网探索

推荐关注此文作者公众号:丰极,关注后回复“面试资料”即可获取百度阿里美团等大厂面试资料。
属性复制神器-mapstruct的更多相关文章
- C#反射——模仿BeanUtil属性复制
反射工具类请参见:https://www.cnblogs.com/threadj/p/10535796.html using System; using System.Collections.Gene ...
- 利用Java反射实现JavaBean对象相同属性复制并初始化目标对象为空的属性的BeanUtils
有时遇到将数据传输对象转换成JSON串会将属性值为空的属性去掉,利用Java反射实现JavaBean对象数据传输对象的相同属性复制并初始化数据传输对象属性为空的属性,然后转换成JSON串 packag ...
- cp 带着属性复制过去,
sudo cp -ra store_bak/* store/ -r 所有文件循环都复制 -a 带着属性复制过去
- neo4j 将一个节点的属性复制到另一个节点上
在使用Python操作Neo4j数据库的时候,经常会遇到重复的节点,需要将一个节点的属性复制到另一个节点,之后将该节点删除. def copy_node_properties(source_node_ ...
- 跨浏览器复制神器 ZeroClipboard 2.x快速入门详解
有些时候,我们希望让用户在网页上完成某个操作就能自动将指定的内容复制到用户计算机的剪贴板中.但是出于安全原因,大多数现代浏览器都未提供通用的剪贴板复制接口(或即便有,也默认被禁用).只有IE浏览器可以 ...
- 使用BeanUtils类实现DTO之间的同名属性复制
开发中经常碰到这样的场景,从数据库查询出来全部的字段,但是有些字段是不想给 客户端看到,这时就需要将属性从DAO复制到传给客户端的DTO对象,如果采用get/set, 那显得很麻烦.可使用反射实现. ...
- bean属性复制到另外一个bean
import org.springframework.beans.BeanUtils; BeanUtils.copyProperties(maker.getBaseInfo(), newBasInfo ...
- 一个高性能的对象属性复制类,支持不同类型对象间复制,支持Nullable<T>类型属性
由于在实际应用中,需要对大量的对象属性进行复制,原来的方法是通过反射实现,在量大了以后,反射的性能问题就凸显出来了,必须用Emit来实现. 搜了一圈代码,没发现适合的,要么只能在相同类型对象间复制,要 ...
- Dozer 实现对象间属性复制
使用场景:两个领域之间对象转换. 比如:在系统分层解耦过程中, 对外facade接口,一般使用VO对象,而内core业务逻辑层或者数据层通常使用Entity实体. VO对象 package com.m ...
随机推荐
- 根据现有Bitmap生成相同图案指定大小的新Bitmap
通过一张现有的Bitmap,画出一张同样的但是大小使我们指定的Bitmap 需求:直接createBitmap的话不允许生成的bitmap的宽高大于原始的,因此需要特定方法来将一张Bitmap的大小进 ...
- qt程序添加文件版本号
1.需要一个 *.rc 文件,用以保存相关信息.比如添加一个 app.rc 里面内容如下所示: IDI_ICON1 ICON DISCARDABLE "app.ico" ----- ...
- 数值格式化 NumberFormat、 DecimalFormat、 RoundingMode
NumberFormat [简介] java.text.NumberFormat extends java.text.Format extends java.lang.Object 实现的接口:Ser ...
- netty集群(一)-服务注册发现
上篇文章介绍了如何搭建一个单机版本的netty聊天室:https://www.jianshu.com/p/f786c70eeccc. 一.需要解决什么问题: 当连接数超过单机的极限时,需要将netty ...
- 检查*.ldf为何这么大
testdb,只是个测试用文件,备份时突然发现*.ldf怎么这么大,当硬盘不要花银子买啊......--可随意删除...,有空再检查,累了休息... 如批量生成数据.或导入那个来自MySQL的Empl ...
- (五)TestNG测试的并发执行详解
原文链接:https://blog.csdn.net/taiyangdao/article/details/52159065 TestNG在执行测试时,默认suitethreadpoolsize=1, ...
- 【JMeter_14】JMeter逻辑控制器__交替控制器<Interleave Controller>
交替控制器<Interleave Controller> 业务逻辑: 根据被控制器触发执行次数,去依次执行控制器下的子节点<逻辑控制器.采样器>. 被触发执行可以由线程组的线程 ...
- 为什么启动线程是start方法?
为什么启动线程是start方法 十年可见春去秋来,百年可证生老病死,千年可叹王朝更替,万年可见斗转星移. 凡人如果用一天的视野,去窥探百万年的天地,是否就如同井底之蛙? 背景:启动线程是start ...
- Shiro简单加密服务
编码/加密 在涉及到密码存储问题上,应该加密 / 生成密码摘要存储,而不是存储明文密码.比如之前的 600w csdn 账号泄露对用户可能造成很大损失,因此应加密 / 生成不可逆的摘要方式存储. Sh ...
- skywalking面板功能介绍2
场景: spring-user调用spring-order 1.spring-user部署了两个应用实例 2.spring-order部署了一个实例 应用详情信息在表 从上面表中可以看出spring- ...