烦人的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 用于继承配置高级使用
多对一
类型转换