还在用 BeanUtils来作对象转换吗?快试试 MapStruct吧


做者:阿进的写字台java

https://www.cnblogs.com/homejim/git

第一次看到 MapStruct 的时候, 我我的很是的开心。由于其跟我心里里面的想法不谋而合。github

1 MapStruct 是什么?

1.1 JavaBean 的困扰

对于代码中 JavaBean之间的转换, 一直是困扰我好久的事情。web

在开发的时候我看到业务代码之间有不少的 JavaBean 之间的相互转化, 很是的影响观感, 却又不得不存在。我后来想的一个办法就是经过反射, 或者本身写不少的转换器。面试

第一种经过反射的方法确实比较方便, 可是如今不管是 BeanUtils, BeanCopier 等在使用反射的时候都会影响到性能。虽然咱们能够进行反射信息的缓存来提升性能。缓存

可是像这种的话, 须要类型和名称都同样才会进行映射, 有不少时候, 因为不一样的团队之间使用的名词不同, 仍是须要不少的手动 set/get 等功能。安全

第二种的话就是会很浪费时间, 并且在添加新的字段的时候也要进行方法的修改。不过, 因为不须要进行反射, 其性能是很高的。微信

1.2 MapStruct 带来的改变

MapSturct 是一个生成类型安全, 高性能且无依赖的 JavaBean 映射代码的注解处理器(annotation processor)。app

抓一下重点:编辑器

  1. 注解处理器

  2. 能够生成 JavaBean 之间那的映射代码

  3. 类型安全, 高性能, 无依赖性

从字面的理解, 咱们能够知道, 该工具能够帮咱们实现 JavaBean 之间的转换, 经过注解的方式。

同时, 做为一个工具类,相比于手写, 其应该具备便捷, 不容易出错的特色。

2 MapStruct 入门

入门很简单。我是基于 Maven 来进行项目 jar 包管理的。

2.1 引入依赖

<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>

2.2 建立entity和dto对象

该类是从 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;


}

2.3 写 Mapper

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 注解便可。

而后方法上, 入参对应要被转化的对象, 返回值对应转化后的对象, 方法名称可任意。

2.4 测试

写一个测试类测试一下。

@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 MapStruct 分析

上面中, 我写了3个步骤来实现了从 OrderOrderQueryParam 的转换。

那么, 做为一个注解处理器, 经过MapStruct 生成的代码具备怎么样的优点呢?

3.1 高性能

这是相对反射来讲的, 反射须要去读取字节码的内容, 花销会比较大。而经过 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;
    }
}

能够看到其生成了一个实现类, 而代码也相似于咱们手写, 通俗易懂。

3.2 易于 debug

在咱们生成的代码中, 咱们能够轻易的进行 debug。

易于 DEBUG


在使用反射的时候, 若是出现了问题, 不少时候是很难找到是什么缘由的。

3.3 使用相对简单

若是是彻底映射的, 使用起来确定没有反射简单。用相似 BeanUtils 这些工具一条语句就搞定了。可是,若是须要进行特殊的匹配(特殊类型转换, 多对一转换等), 其相对来讲也是比较简单的。

基本上, 使用的时候, 咱们只须要声明一个接口, 接口下写对应的方法, 就可使用了。固然, 若是有特殊状况, 是须要额外处理的。

3.4 代码独立

生成的代码是对立的, 没有运行时的依赖。


--------------------------   END  --------------------------


原创整理《第2版:互联网大厂面试题》



扫码添加,个人我的微信,备注: 面试题
 注意,不要乱回复 
(必定要备注: 面试题 )不然不给经过

若是被添加频繁,请30分钟后再试试

没错,加我好友,给你安排到位

本文分享自微信公众号 - Java研发军团(ityuancheng)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索