烦人的Bean 转换

一直困扰着我代码中JavaBean之间的转换。 在开发过程中,我们看到业务代码之间有很多JavaBean之间的相互转换,对外观有很大的影响,但必须存在。 我之后想的一个方法是反射和自己写很多转换器。

第一种反射方法确实很有用,但现在BeanUtils、BeanCopier等也在使用反射时会影响性能。 虽然可以缓存反射信息以提高性能。 但是,在这种情况下,如果类型和名称不相同,就无法进行映射,各个团队使用的名词不同,因此大多需要手动set/get等功能。

第二,时间很浪费,在添加新字段时也要重新审视方法。 但是,由于不需要进行反射,所以其性能很高。

MapStruct 带来的改变

MapSturct是一个注释处理器(annotation processor ),用于生成类型安全、高性能和无相关性的JavaBean映射代码。

抓住要点:

注释处理器可以从JavaBean之间映射代码类型的安全性、高性能、无相关性的字面上理解,我们知道该工具有助于通过注释实现JavaBean之间的转换。

另外,作为工具类,与手写相比,应该有方便、不易出错的特征。

MapStruct 入门

依赖关系的部署

从属关系

组org.mapstruct /组id

artifactidmapstruct-JDK8/artifact id

版本1.3.1 .最终/版本

/从属关系

从属关系

组org.mapstruct /组id

artifactidmapstruct -处理器/影响标识

版本1.3.1 .最终/版本

/dependency我现在正在将系统对接。 收到的是支付信息的支付信息。 后面的数据是TExpensesRecords消费记录。 需要转换bean。 如果我一点一点地写get/set真的很麻烦,是没有头脑的体力劳动。

中间涉及许多类型转换、嵌套等复杂的操作,但我们想要的只是建立它们之间的映射关系。 有解决这一切的通用映射工具吗? 当然有,也有很多。 据说apache的BeanUtil.copyProperties可以实现,但是性能不好而且容易发生异常,很多规格中严禁使用这种方法。 以下是几个对象映射框架的比较,在大多数情况下,MapStruct的性能最高。 原理类似于lombok。 MapStruct是在编译时实现的,基于Getter、Setter,没有使用反射,所以一般不存在运行时性能问题。

测试

我现在有两节课

一个是接口VO类

@Data

@ApiModel (“支付信息”

公共类支付信息

@ApiModelProperty (“支付代码”

私有字符串支付代码;

@ApiModelProperty (“总额”) )。

私有字符串总货币;

@ApiModelProperty (“料理”

私人列表食品;

@ apimodelproperty (值='提供者id ' ) ) ) ) ) ) ) ) ) ) ) ) )。

专用字符串服务id;

@ apimodelproperty (值='提供者名称') )。

专用字符串服务名称;

@apimodelproperty(value='餐馆编号') ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) )。

私有字符串恢复代码;

@ apimodelproperty (值='餐厅名称') )。

私有字符串恢复名称;

@ apimodelproperty (值='机器编号') )。

私有字符串机器代码;

@apimodelproperty(value='消费类型(1:食堂吃饭、2:店铺消费、自动售货机消费) )

私有字符串费用类型;

}一个是数据库DO类

@Data

@ API model (值=' texpensesrecords ',说明='消费记录表') )。

公共类

TExpensesRecords implements Serializable {   @ApiModelProperty(value = "")   private Integer id;   @ApiModelProperty(value = "交易流水号(随便生成唯一编号)")   private String tradeId;   @ApiModelProperty(value = "员工号")   private String userCode;   @ApiModelProperty(value = "服务商id")   private String serviceId;   @ApiModelProperty(value = "服务商名称")   private String serviceName;   @ApiModelProperty(value = "餐厅编号")   private String restaurantCode;   @ApiModelProperty(value = "餐厅名称")   private String restaurantName;   @ApiModelProperty(value = "机器号")   private String machineCode;   @ApiModelProperty(value = "消费类型(1:食堂用餐、2:网点消费、3:自助贩卖机消费)")   private String expensesType;   @ApiModelProperty(value = "消费金额")   private BigDecimal amount;   @ApiModelProperty(value = "消费时间")   private LocalDateTime expensesDate;

可以看到,他们有一些属性是同名的。

我们一步步来写。

先写一个转换接口

写一个测试类

@Test public void payInfo2ExpenseRecord() throws Exception {   PayInfo payInfo = new PayInfo();   payInfo.setPayCode("20200513_01471111_120_40_1589359038744");   payInfo.setTotalMoney("39.3");   payInfo.setServiceId("001");   payInfo.setServiceName("服务商A");   payInfo.setRestaurantCode("A001");   payInfo.setRestaurantName("餐厅A");   payInfo.setMachineCode("M001");   payInfo.setExpensesType("1");   TExpensesRecords tExpensesRecords =           PayInfoMapper.INSTANCE.payInfo2ExpenseRecord(payInfo);   log.info(tExpensesRecords.toString()); }

可以看到,大部分相同名字的属性已经完成转换。但是名字不一致的,需要我们单独配置下。

处理不同名的属性

代码也很好理解,就是将源的payCode字段映射到目标的tradeId字段

看下测试结果

TExpensesRecords(id=null, tradeId=20200513_01471111_120_40_1589359038744, userCode=null, serviceId=001, serviceName=服务商A, restaurantCode=A001, restaurantName=餐厅A, machineCode=M001, expensesType=1, amount=39.3, expensesDate=null)

tradeId映射成功。而且totalMoney是String,映射成BigDecimal的amount也成功了

原理

原理类似于lombok ,MapStruct都是在编译期对接口进行实现,而且基于Getter、Setter,没有使用反射所以一般不存在运行时性能问题。 类型不同,会自动进行转换。

Spring 注入的方式

上面的例子是默认的方式

  PayInfoMapper INSTANCE = Mappers.getMapper(PayInfoMapper.class);

在正常的项目中,一般和spring整合使用

就是在 @Mapper 后面加入 componentModel="spring"

在用到的地方就可以使用@Autowired注入了

注解说明

@Mapper 只有在接口加上这个注解, MapStruct 才会去实现该接口   @Mapper 里有个 componentModel 属性,主要是指定实现类的类型,一般用到两个   default:默认,可以通过 Mappers.getMapper(Class) 方式获取实例对象   spring:在接口的实现类上自动添加注解 @Component,可通过 @Autowired 方式注入 @Mapping:属性映射,若源对象属性与目标对象名字一致,会自动映射对应属性   source:源属性   target:目标属性   dateFormat:String 到 Date 日期之间相互转换,通过 SimpleDateFormat,该值为 SimpleDateFormat             的日期格式   ignore: 忽略这个字段 @Mappings:配置多个@Mapping @MappingTarget 用于更新已有对象 @InheritConfiguration 用于继承配置

高级使用

多对一

类型转换