mapStruct学习笔记

时间:2022-07-23
本文章向大家介绍mapStruct学习笔记,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

mapStruct是一个专门进行数据类型转化工具,与ModelMapper和BeanUtils的反射不同,这个MapStruct抛弃了动态生成的策略,反而采用了类似代理的方式,编译的时候进行动态生成,也就是说在编译的时候,就把要我们需要的get、set都已经写到了“代理类”的class文件里了。然后代码运行的时候,直接使用的是代理类。大概得过程mapstruct根据一般的规范定义一套解析规则,然后开发人员按规则去写,然后编译的时候,mapstruct按这个规则生成一个类,这个类里包含了实体转化的各种set、get方法。

通过编译,我们可以找到这个代理生成的类。

mapstruct整合SpringBoot

导入maven依赖,这里的版本号采用用的比较多的1.3.1.Final

            <dependency>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct</artifactId>
                <version>${mapstruct.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>${mapstruct.version}</version>
            </dependency>

定义接口(这里先留个疑问,为啥要定义接口)

注意:我们为了方便找到要映射的实体和业务模块,因此我们把映射的接口写到业务实体包里。接口的命名与映射的实体保持一致,仅在接口的命名最后添加Mapper即可。

接口的定义采用以业务为导向,比如request2Dto,表示从web层转化到Service层。使用这一个方法即可。没有必须请不要定义多余的接口。

mapStruct的使用

SearchDto dto  = SearchAdvanceRequestMapper.INSTANCE.request2Dto(searchAdvanceRequest);

mapStruct异名字段映射

异名字段的映射需要在接口的转化接口中指定来源实体的字段和目标实体的字段信息。这里使用`@Mapping`注解把uid转换到dto中的userId字段

@Mapper
public interface SearchAdvanceRequestMapper {
    /**
     * 映射实体
     */
    SearchAdvanceRequestMapper INSTANCE = Mappers.getMapper(SearchAdvanceRequestMapper.class);
    /***
     * 数据转化
     * @param request 请求
     * @return dto
     */
    @Mapping(source = "uid", target = "userId")
    SearchDto request2Dto(SearchAdvanceRequest request);
}

mapStruct虚方法自定义

注意:这里的虚方法是java8的特性。

如果采用自定义的话,就直接用自定义了。所以我们将自定义的mapstruct最后用在小部分数据上。

这里把小部分的数据中的request的实体转换成dto中的ageDto属性,并对属性进行改变。这里把int转化为了string类型

    @Mapping(source = "uid", target = "userId")
    @Mapping(source = "request", target = "age")
    SearchDto request2Dto(SearchAdvanceRequest request);

    default AgeDto request2Dto1(SearchOrdinaryRequest request) {
        AgeDto dto = new AgeDto();
        dto.setAge(String.valueOf(request.getAge()));
        return dto;
    }

mapStruct多转一

有些时候,我们需要将多个实体转化到一个实体中去。就是说我们传入两个实体,然后转化的结果是一个实体。

@Mapper
public interface UserModifyRequestMapper {

    /**
     * 映射实体
     */
    UserModifyRequestMapper INSTANCE = Mappers.getMapper(UserModifyRequestMapper.class);


    /**
     * 多对一测试
     * @param request request
     * @param userPasswordRequest passwrod
     * @return dto
     */
    @Mapping(source = "request.userAge", target = "age")
    @Mapping(source = "userPasswordRequest.password", target = "userPassword")
    UserDto request2Dto(UserAddRequest request, UserPasswordRequest userPasswordRequest);
}

Mappers.getMapper(A.class)解析

这个类主要就是通过类加载器,加载所有的类,然后选择以传入类名+impl结尾的类。所以返回都是mapStruct生成的实现类,如文章第一图一样的impl类。

    private static <T> T doGetMapper(Class<T> clazz, ClassLoader classLoader) throws NoSuchMethodException {
        try {
            Class<T> implementation = classLoader.loadClass(clazz.getName() + "Impl");
            Constructor<T> constructor = implementation.getDeclaredConstructor();
            constructor.setAccessible(true);
            return constructor.newInstance();
        } catch (ClassNotFoundException var4) {
            return getMapperFromServiceLoader(clazz, classLoader);
        } catch (InvocationTargetException | IllegalAccessException | InstantiationException var5) {
            throw new RuntimeException(var5);
        }
    }

mapStruct实现机制探索

mapStruct采用java的apt(注解解析器)来实现的,也就是要继承AbstractProcessor类,然后重写init和process方法,当然process方法的参数就是包含了所有注解的类,然后在process中进行处理。这里先解析,然后重新write到目录去了。也就是上边说的impl后缀的目标文件。