第一次看到 MapStruct 的时候, 我我的很是的开心。由于其跟我心里里面的想法不谋而合。java
对于代码中 JavaBean之间的转换, 一直是困扰我好久的事情。在开发的时候我看到业务代码之间有不少的 JavaBean 之间的相互转化, 很是的影响观感, 却又不得不存在。我后来想的一个办法就是经过反射, 或者本身写不少的转换器。整理了一份Java面试宝典完整版PDF已整理成文档git
第一种经过反射的方法确实比较方便, 可是如今不管是 BeanUtils, BeanCopier 等在使用反射的时候都会影响到性能。虽然咱们能够进行反射信息的缓存来提升性能。可是像这种的话, 须要类型和名称都同样才会进行映射, 有不少时候, 因为不一样的团队之间使用的名词不同, 仍是须要不少的手动 set/get 等功能。github
第二种的话就是会很浪费时间, 并且在添加新的字段的时候也要进行方法的修改。不过, 因为不须要进行反射, 其性能是很高的。面试
MapSturct 是一个生成类型安全, 高性能且无依赖的 JavaBean 映射代码的注解处理器(annotation processor)。缓存
抓一下重点:安全
注解处理器app
能够生成 JavaBean 之间那的映射代码ide
从字面的理解, 咱们能够知道, 该工具能够帮咱们实现 JavaBean 之间的转换, 经过注解的方式。工具
同时, 做为一个工具类,相比于手写, 其应该具备便捷, 不容易出错的特色。性能
入门很简单。我是基于 Maven 来进行项目 jar 包管理的。
<properties> <org.mapstruct.version>1.3.0.Final</org.mapstruct.version> </properties> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-jdk8</artifactId> <version>${org.mapstruct.version}</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </dependency>
该类是从 github 某个订单系统里面拿下来的部分。
@Data public class Order { /** *订单id */ private Long id; /** * 订单编号 */ private String orderSn; /** * 收货人姓名/号码 */ private String receiverKeyword; /** * 订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单 */ private Integer status; /** * 订单类型:0->正常订单;1->秒杀订单 */ private Integer orderType; /** * 订单来源:0->PC订单;1->app订单 */ private Integer sourceType; }
对应的查询参数
@Data public class OrderQueryParam { /** * 订单编号 */ private String orderSn; /** * 收货人姓名/号码 */ private String receiverKeyword; /** * 订单状态:0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单 */ private Integer status; /** * 订单类型:0->正常订单;1->秒杀订单 */ private Integer orderType; /** * 订单来源:0->PC订单;1->app订单 */ private Integer sourceType; }
Mapper 即映射器, 通常来讲就是写 xxxMapper 接口。固然, 不必定是以 Mapper 结尾的。只是官方是这么写的。在本入门例子中,对应的接口以下
import com.homejim.mapstruct.dto.OrderQueryParam; import com.homejim.mapstruct.entity.Order; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @Mapper public interface OrderMapper { OrderQueryParam entity2queryParam(Order order); }
简单的映射(字段和类型都匹配), 只有一个要求, 在接口上写 @Mapper 注解便可。而后方法上, 入参对应要被转化的对象, 返回值对应转化后的对象, 方法名称可任意。
写一个测试类测试一下。
@Test public void entity2queryParam() { Order order = new Order(); order.setId(12345L); order.setOrderSn("orderSn"); order.setOrderType(0); order.setReceiverKeyword("keyword"); order.setSourceType(1); order.setStatus(2); OrderMapper mapper = Mappers.getMapper(OrderMapper.class); OrderQueryParam orderQueryParam = mapper.entity2queryParam(order); assertEquals(orderQueryParam.getOrderSn(), order.getOrderSn()); assertEquals(orderQueryParam.getOrderType(), order.getOrderType()); assertEquals(orderQueryParam.getReceiverKeyword(), order.getReceiverKeyword()); assertEquals(orderQueryParam.getSourceType(), order.getSourceType()); assertEquals(orderQueryParam.getStatus(), order.getStatus()); }
测试经过, 没有任何的问题。
上面中, 我写了3个步骤来实现了从 Order 到 OrderQueryParam 的转换。
那么, 做为一个注解处理器, 经过MapStruct 生成的代码具备怎么样的优点呢?
这是相对反射来讲的, 反射须要去读取字节码的内容, 花销会比较大。而经过 MapStruct 来生成的代码, 其相似于人手写。速度上能够获得保证。
前面例子中生成的代码能够在编译后看到。在 target/generated-sources/annotations 里能够看到。
生成的代码
对应的代码
@Generated( value = "org.mapstruct.ap.MappingProcessor", date = "2019-08-02T00:29:49+0800", comments = "version: 1.3.0.Final, compiler: javac, environment: Java 11.0.2 (Oracle Corporation)" ) public class OrderMapperImpl implements OrderMapper { @Override public OrderQueryParam entity2queryParam(Order order) { if ( order == null ) { return null; } OrderQueryParam orderQueryParam = new OrderQueryParam(); orderQueryParam.setOrderSn( order.getOrderSn() ); orderQueryParam.setReceiverKeyword( order.getReceiverKeyword() ); orderQueryParam.setStatus( order.getStatus() ); orderQueryParam.setOrderType( order.getOrderType() ); orderQueryParam.setSourceType( order.getSourceType() ); return orderQueryParam; } }
能够看到其生成了一个实现类, 而代码也相似于咱们手写, 通俗易懂。
在咱们生成的代码中, 咱们能够轻易的进行 debug。
易于 DEBUG
在使用反射的时候, 若是出现了问题, 不少时候是很难找到是什么缘由的。
若是是彻底映射的, 使用起来确定没有反射简单。用相似 BeanUtils 这些工具一条语句就搞定了。可是,若是须要进行特殊的匹配(特殊类型转换, 多对一转换等), 其相对来讲也是比较简单的。整理了一份Java面试宝典完整版PDF已整理成文档
基本上, 使用的时候, 咱们只须要声明一个接口, 接口下写对应的方法, 就可使用了。固然, 若是有特殊状况, 是须要额外处理的。
生成的代码是对立的, 没有运行时的依赖。