顶级工具,性能爆棚的 Java 实体转换 / 复制神器

互联网架构师

共 11994字,需浏览 24分钟

 · 2023-11-01

因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享

点击关注#互联网架构师公众号,领取架构师全套资料 都在这里
0、2T架构师学习资料干货分

上一篇:2T架构师学习资料干货分享

大家好,我是互联网架构师!
 

Java项目中实体转换无处不在,当实体字段较多或者大批量的进行复制时,通过手工setter/getter显得太LOW,同时兼备高性能要求情况下,MapStruct完全完全能够胜任。

官方解释,MapStruct是一个代码生成器,它基于约定优于配置的方法,极大地简化了Java bean类型之间映射的实现。生成的映射代码使用普通方法调用,因此快速、类型安全且易于理解。因为MapStruct是在编译期间生成setter/getter方法,实际运行时就是直接调用setter/getter,效率会非常高。

优点

  • MapStruct编译期生成映射代码,所以可以在编译时暴露映射错误的代码,让错误提前暴露;

  • 因为使用setter/getter方式,而非反射方式,所以可以更快的执行效率;

  • 可以实现深拷贝,自动类型转换,如枚举转换;

  • 进行自定义的映射,多种映射方式,下边具体说明;

性能对比

对比对象

10个对象复制1次

1万个对象复制1次

100万个对象复制1次

100万个对象复制5次

MapStruct

0ms

3ms

96ms

281ms

Hutools的BeanUtil

23ms

102ms

1734ms

8316ms

Spring的BeanUtils

2ms

47ms

726ms

3676ms

Apache的BeanUtils

20ms

156ms

10658ms

52355ms

Apache的PropertyUtils

5ms

68ms

6767ms

30694ms

使用

依赖

<!-- MapStruct核心,包含了一些必要的注解-->
<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                   <!-- MapStruct编译,注解处理器,根据注解自动生成Mapper的实现 -->
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

定义转换接口

/**
 * 测试接口
 *
 * @author reboot
 */

@Mapper
public interface OrderConvertor {

    /**
     * 实例
     */

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

    /**
     * OrderDo -> OrderModel
     *
     * @param orderDo 订单实体
     * @return {@link OrderModel}
     */

    OrderModel toModel(OrderDo orderDo);

    /**
     * OrderDo -> OrderModel
     *
     * @param orderDos 订单实体
     * @return {@link OrderModel}
     */

    List<OrderModel> toModel(List<OrderDo> orderDos);

    /**
     * OrderModel -> OrderDo
     *
     * @param orderModel 订单模型
     * @return {@link OrderDo}
     */

    OrderDo toDo(OrderModel orderModel);

    /**
     * OrderModel -> OrderDo
     *
     * @param orderModels 订单模型
     * @return {@link OrderDo}
     */

    List<OrderDo> toDo(List<OrderModel> orderModels);
}

编译结果

 

MapStruct会自动生成对应接口的实现,并自动完成属性映射关系,List会自动进行批量处理。

调用

/**
 * 订单服务
 *
 * @author reboot
 */

@Service
public class OrderService {

    /**
     * 获取订单列表
     *
     * @return {@link List}<{@link OrderModel}>
     */

    public List<OrderModel> getOrderList() {
        // 获取数据库数据DO
        List<OrderDo> result = selectOrderList();
        // 参数转换
        return OrderConvertor.INSTANCE.toModel(result);
    }
}

插件

 

上边的使用方式虽然能够正常使用,但是在一些属性配置映射上和提示上,如果使用插件能够提升使用体验,IDEA中可以直接安装Mapstruct Support插件,当然Eclipse也有对应的插件。

特性

  • 突出显示目标属性和源属性。将目标属性和源属性转到声明的setter / getter中;

  • 错误和快速修复:

  • 缺少@Mapper或@MapperConfig注解检查;

  • 快速修复未映射的目标属性,添加未映射目标属性和忽略未映射目标属性;

 

其他用法

更加详细的内容可以查看官方文档,发布文章时最新版本是 MapStruct 1.5.3.Final.html。

基础映射

@Mapper
public interface CarMapper {

    @Mapping(target = "manufacturer", source = "make")
    @Mapping(target = "seatCount", source = "numberOfSeats")
    CarDto carToCarDto(Car car);

    @Mapping(target = "fullName", source = "name")
    PersonDto personToPersonDto(Person person);
}

target表示目标属性名,source表示源属性名,一般在目标属性和源属性不同时使用,相同的属性名会自动进行映射。

映射器添加自定义方法

@Mapper
public interface CarMapper {

    @Mapping(...)
    ...
    CarDto carToCarDto(Car car);

    default PersonDto personToPersonDto(Person person) {
        //hand-written mapping logic
    }
}

自定义方法personToPersonDto并实现,在生成的实现类中会进行覆盖使用。

多个源参数映射

@Mapper
public interface AddressMapper {

    @Mapping(target = "description", source = "person.description")
    @Mapping(target = "houseNumber", source = "address.houseNo")
    DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Address address);

    @Mapping(target = "description", source = "person.description")
    @Mapping(target = "houseNumber", source = "hn")
    DeliveryAddressDto personAndAddressToDeliveryAddressDto(Person person, Integer hn);
}

存在多个源参数,使用参数名.属性名的方式进行表示,也可以直接使用基础类型的属性名称。

嵌套属性映射到当前目标

@Mapper
 public interface CustomerMapper {

     @Mapping( target = "name", source = "record.name" )
     @Mapping( target = ".", source = "record" )
     @Mapping( target = ".", source = "account" )
     Customer customerDtoToCustomer(CustomerDto customerDto);
 }

当源参数中存在对象属性,可以手动进行映射,或者直接使用"."的方式将对象中的属性全部映射到当前目标对象。

表达式方式

@Mapper
public interface SourceTargetMapper {

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

    @Mapping(
        target = "timeAndFormat",
        expression = "java( new org.sample.TimeAndFormat( s.getTime(), s.getFormat() ) )"
    )
    Target sourceToTarget(Source s);
}

支持使用java代码块进行转换,一般可以将静态方法处理的字段放到这里。

更新现有实例

@Mapper
public interface CarMapper {

    void updateCarFromDto(CarDto carDto, @MappingTarget Car car);
}

@MappingTarget源参数,编译时会将carDto参数中的属性映射到car参数中。

Map映射

@Mapper
public interface CustomerMapper {

    @Mapping(target = "name", source = "customerName")
    Customer toCustomer(Map<String, String> map);
}

直接将map中的key进行映射。

更多用法

还有更多其他用法,比如:

  • 支持映射定义的public属性;

  • 支持映射参数Builder模式;

  • 使用注入方式引入转换器;

  • 数据类型字段转换,如枚举、日期,支持日期格式化,支持数字类型格式化,具体可以看 Implicit type conversions;

  • 集合类型自动转换;

  • 转换Stream;

  • ......

总结

MapStruct还有很多其他高阶特性,限于篇幅文章仅仅列举部分示例,有兴趣的同学可以查看对应文档试试。使用适当的工具有效提高编程效率,在使用工具过程中我们也了解其实现原理,不断提高自身。后边有时间也把MapStruct实现原理拿出来讲讲,跟大家一起学习进步!


1、2T架构师学习资料干货分享

2、10000+TB 资源,阿里云盘,牛逼!!

3、基本涵盖了Spring所有核心知识点总结

  · END ·

最后,关注公众号互联网架构师,在后台回复:2T,可以获取我整理的 Java 系列面试题和答案,非常齐全

如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描上方二维码关注一下,您的支持是我坚持写作最大的动力。

求一键三连点赞、转发、在看

浏览 426
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报