文章目录
前言
你你是否已经对手动get/set感到厌烦、恶心。什么???你就喜欢写这种代码?那没事了
最近做项目遇到了要做大量的PO,VO,DTO之间的转换,所以一开始想到了Spring BeanUtils 但那个不能进行集合的转换,所以问了我朋友,他给我推荐了MapStruct;
一、为什么选择MapStruct
工具 | 实现方式 | 缺点 | 说明 | 速度(100w) |
---|---|---|---|---|
MapStruct | getter/setter方法 | 需要了解注解和配置项语法 | JSR269注解处理器在编译期自动生成Java Bean转换代码,支持可配置化,扩展性强 | 54ms |
orika | 动态生成字节码 | 首次调用耗时较久,性能适中 | 采用javassist类库生成Bean映射的字节码,之后直接加载执行生成的字节码文件 | 6764ms |
Spring BeanUtils | 反射机制 | 不支持名称相同但类型不同的属性转换 | 采用javassist类库生成Bean映射的字节码,之后直接加载执行生成的字节码文件 | 6700ms |
Apache BeanUtils | 反射机制 | 需要处理编译期异常,性能最差 | 7086ms | |
dozer | 反射机制 | 性能差 | 使用reflect包下Field类的set(Object obj, Object value)方法进行属性赋值 | 6974ms |
BeanCopier | 反射机制 | BeanCopier只拷贝名称和类型都相同的属性。即便基本类型与其对应的包装类型也不能相互转换 | 使用ASM的MethodVisitor直接编写各属性的get/set方法 | 6834ms |
就性能而言:手动get/set > MapStruct > Spring BeanUtils > orika > BeanCopier > dozer > apache BeanUtils
二、MapStruct使用步骤
1、引入依赖
<!-- mapStruct开始 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.4.2.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</dependency>
<!-- mapStruct结束 -->
2、新建一个抽象类或者接口并标注@Mapper
在其中写一个转换的方法,方法名字是自定义的
获取对象INSTANCE并使用
//这是个对象转换的类
@Mapper
public abstract class CarConvert(){
/**
* 提供一个获取CarConvert对象实例的静态属性
*/
public static CarConvert INSTANCE = Mappers.getMapper(CarConvert.class)
/**
* 对象转换的方法
*/
public abstract CarVo abc(CarDTo carDTo);
}
3、测试
- 其中@Mapper的默认映射规则
- 同类型且同名的属性,会自动映射
- 且Mapstruct会自动进行类型的转换
@Test
public void test(){
CarDTO carDTO = new CarDTO();
//直接调用转换的方法,进行对象的转换
CarVO carVO = CarConvert.INSTANCE.abc(carDTO);
System.out.println(carVO)
}
4、可以通过@Mappings和@Mapping处理映射规则(自定义映射规则)
如果某个属性不想映射给VO,则可以在映射中设置 ignore=true
如果想对已有对象赋值,使用@MappingTarget 注解表示传过来的CarVO对象是已经赋值过的
//这是个对象转换的类
//其中Mapstruct整合spring需要添加一些属性在@Mapping(componentModel = "spring")
@Mapper(componentModel = "spring")
public abstract class CarConvert(){
//提供一个获取CarConvert对象实例的静态属性(整合spring,这句话就可以不需要了)
public static CarConvert INSTANCE = Mappers.getMapper(CarConvert.class)
@Mappings(
value = {
//指定金额的映射规则,数字格式化numberFormat,保留两位小数
@Mapping(source = "totalPrice",target = "totalPrice",numberFormat = "#.00"),
//指定日期的映射规则,日期格式化dateFormat
@Mapping(source = "publishDate",target = "publishDate",dateFormat = "yyyy-MM-dd HH:mm:ss"),
//color属性不想映射的设置
@Mapping(target = "color",ignore = true)
//属性名不同的映射
@Mapping(source = “brand”,target = “brandName”)
//对象里面包含,另一个对象的映射处理
@Mapping(source = “driverDTO”,target = “driverVO”)
}
)
//对象转换的方法对象转换的方法
public abstract CarVo abc(CarDTo carDTo);
//对象中 包含的对象 的转换方法
@Mapping(source = “id”,target = “driverId”)
@Mapping(source = “name”,target = “fullName”)
public abstract DriverVO driverDTO2driverVO(DriverDTO driverDTO);
//@AfterMapping 添加这个注解表示mapstruct在调用完自动转换的方法后,会自动调用本方法
//@MappingTarget 注解表示传过来的CarVO对象是已经赋值过的
@AfterMapping
public void dto2voAfter(CarDTO carDTO, @MappingTarget CarVO carVO){
List<PartDTO> partDTOS = carDTO.getPartDTOS();
boolean hasPart = partDTOS != null && !partDTOS.isEmpty();
carVO.setHasPart(hasPart);
}
//集合对象的批量转换
public abstract List<CarVo> dto2Vos(List<CarDTO> carDTO);
//@BeanMapping(ignoreByDefault = true) 表示开启 忽略mapstruct的默认映射行为,只有配置了@Mapping注解的才能映射
@BeanMapping(ignoreByDefault = true)
@Mapping(source = “id”,target = “id”)
public abstract VehicleVO carDTO2vehicleVO(CarDTO carDTO);
}
三、遇到的问题
- 当出现
Unknown property "xxx" in result type xxx. Did you mean "null"?
错误时添加该依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
<scope>provided</scope>
</dependency>