MapStruct 高效-安全-优雅实现Bean转换

大家好,我是一安,之前听码友说MapStruct做Java之间Bena对象转换,那是相当舒服。

今天有时间,特意尝试了一下,确实不错,值得推荐。

MapStruct

Mapsturct 是一个通过配置公约的一种代码生成器,它大大简化了Java Bean类型之间的映射的实现,而且类型安全,性能高,使用简单

这时有人会提到为什么不直接用Spring的BeanUtils,这里给大家做个简单概述:

  • 如果两个对象之间存在不同字段,则不会对这些字段进行处理
  • 如果两个对象之间字段类型不相同,则会导致类型转换异常
  • 关键性能也比较低MapStruct 高效-安全-优雅实现Bean转换

针对1和2,举个例子

@Data
@AllArgsConstructor
public class Students {
    private String name;
    private String address;
    private List<Integer> ids;
}
@Data
public class StudentsVO {
    private String name;
    private String addr;
    private List<String> ids;
}

可以看出两个实体类,address和addr字段名不一致,ids里字段类型不一致,下面代码验证一下:

    Students students2 = new Students("一安未来2","北京", Arrays.asList(1, 2, 3));
    StudentsVO studentsVO2 = new StudentsVO();
    BeanUtils.copyProperties(students2, studentsVO2);
    System.out.println("BeanUtils:"+studentsVO2);

    List<String> ids = studentsVO2.getIds();
    for (String id:ids){
        System.out.println(id);
    }
    
    //运行结果
    BeanUtils:StudentsVO(name=一安未来2, addr=null, ids=[1, 2, 3])
    Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
       at com.capitek.mapstruct.Test.main(Test.java:26)

但是Mapsturct却可以很好的处理以上问题,而且性能也优于BeanUtils

废话不说,直接上代码

演示

初步验证

1.引入依赖

<dependency>
    <groupId>org.mapstruct</groupId>
    <!-- jdk8以下就使用mapstruct -->
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.2.0.Final</version>
</dependency>

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.2.0.Final</version>
</dependency>

2.定义mapper

/**
 * @Mapper 定义这是一个MapStruct对象属性转换接口,在这个类里面规定转换规则
 *          在项目构建时,会自动生成该接口的实现类,这个实现类将实现对象属性值复制
 * componentModel 属性,它的值就是当前要使用的依赖注入的环境
 */
@Mapper(componentModel = "spring")
public interface StudentMapper {

    //默认加载方式
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
    
     /**
     * 这个方法就是用于实现对象属性复制的方法
     *
     * @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
     * @param students 这个参数就是源对象,也就是需要被复制的对象
     * @return 返回的是目标对象,就是最终的结果对象
     */
    @Mappings({
            @Mapping(source = "address", target = "addr")
    })
    StudentsVO students2StudentsVO(Students students);
}

3.验证

    Students students3 = new Students("一安未来3","北京",Arrays.asList(1, 2, 3));
    StudentsVO studentsVO3 = StudentMapper.INSTANCE.students2StudentsVO(students3);
    System.out.println("MapStruct:"+studentsVO3);
    List<String> ids = studentsVO3.getIds();
    for (String id:ids){
        System.out.println(id);
    }
    
    //运行
    MapStruct:StudentsVO(name=一安未来3, addr=北京, ids=[1, 2, 3])
    1
    2
    3

绑定多个对象的属性值

1.复制实体类Students--->Students2

2.定义mapper

/**
 * @Mapper 定义这是一个MapStruct对象属性转换接口,在这个类里面规定转换规则
 *          在项目构建时,会自动生成该接口的实现类,这个实现类将实现对象属性值复制
 * componentModel 属性,它的值就是当前要使用的依赖注入的环境
 */
@Mapper(componentModel = "spring")
public interface StudentMapper {
    //默认加载方式
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
    
     /**
     * 这个方法就是用于实现对象属性复制的方法
     *
     * @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
     * @param students 这个参数就是源对象,也就是需要被复制的对象
     * @return 返回的是目标对象,就是最终的结果对象
     */
    @Mappings({
            @Mapping(source = "address", target = "addr")
    })
    StudentsVO students2StudentsVO(Students students);

    @Mappings({
            @Mapping(source = "students.name", target = "name"),
            @Mapping(source = "students.address", target = "addr"),
            @Mapping(source = "students2.ids", target = "ids")
    })
    StudentsVO students2StudentsVO(Students students,Students2 students2);
}

3.验证

    Students students = new Students("一安未来3","北京",Arrays.asList(1, 2, 3));
    Students2 students2 = new Students2("一安未来3","北京",Arrays.asList(3, 4, 5));
    StudentsVO studentsVO3 = StudentMapper.INSTANCE.students2StudentsVO(students,students2);
    System.out.println("MapStruct:"+studentsVO3);
    
    //运行
    MapStruct:StudentsVO(name=一安未来3, addr=北京, ids=[3, 4, 5])

参数作为属性值

1.定义mapper

/**
 * @Mapper 定义这是一个MapStruct对象属性转换接口,在这个类里面规定转换规则
 *          在项目构建时,会自动生成该接口的实现类,这个实现类将实现对象属性值复制
 * componentModel 属性,它的值就是当前要使用的依赖注入的环境
 */
@Mapper(componentModel = "spring")
public interface StudentMapper {
    //默认加载方式
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
    
     /**
     * 这个方法就是用于实现对象属性复制的方法
     *
     * @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
     * @param students 这个参数就是源对象,也就是需要被复制的对象
     * @return 返回的是目标对象,就是最终的结果对象
     */
    @Mappings({
            @Mapping(source = "address", target = "addr")
    })
    StudentsVO students2StudentsVO(Students students);

    @Mappings({
            @Mapping(source = "students.name", target = "name"),
            @Mapping(source = "students.address", target = "addr"),
            @Mapping(source = "students2.ids", target = "ids")
    })
    StudentsVO students2StudentsVO(Students students,Students2 students2);


    @Mappings({
            @Mapping(source = "students.name", target = "name"),
            @Mapping(source = "students.address", target = "addr"),
            @Mapping(source = "param", target = "ids")
    })
    StudentsVO students2StudentsVO(Students students, List<String> param);
}

2.验证

    Students students = new Students("一安未来3","北京",Arrays.asList(1, 2, 3));
    StudentsVO studentsVO3 = StudentMapper.INSTANCE.students2StudentsVO(students,Arrays.asList("5""6""7"));
    System.out.println("MapStruct:"+studentsVO3);
    
    //运行
    MapStruct:StudentsVO(name=一安未来3, addr=北京, ids=[5, 6, 7])              

自定义类型转换

1.Students和StudentsVO增加gender字段:Students中为Integer类型 ,StudentsVO中为String类型

2.定义转换类

@Named("genderToString")
public class GenderToString {

    public String toStr(Integer gender) {
        if (gender==0) {
            return "Y";
        } else {
            return "N";
        }
    }

    public Integer toInt(String gender) {
        if ("Y".equals(gender)) {
            return 0;
        } else {
            return 1;
        }
    }
}

3.定义mapper

/**
 * @Mapper 定义这是一个MapStruct对象属性转换接口,在这个类里面规定转换规则
 * 在项目构建时,会自动生成改接口的实现类,这个实现类将实现对象属性值复制
 * componentModel 属性,它的值就是当前要使用的依赖注入的环境
 * 注意当componentModel为spring时,GenderToString也需要交给spring管理
 * uses属性支持设置多个
 */
@Mapper(uses = { GenderToString.class})
public interface StudentMapper {
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
    
     /**
     * 这个方法就是用于实现对象属性复制的方法
     *
     * @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
     * qualifiedByName指明转换类
     * @param students 这个参数就是源对象,也就是需要被复制的对象
     * @return 返回的是目标对象,就是最终的结果对象
     */
    @Mappings({
            @Mapping(source = "address", target = "addr"),
            @Mapping(source = "gender", target = "gender",qualifiedByName= {"genderToString"})
    })
    StudentsVO students2StudentsVO(Students students);

    @Mappings({
            @Mapping(source = "students.name", target = "name"),
            @Mapping(source = "students.address", target = "addr"),
            @Mapping(source = "students2.ids", target = "ids")
    })
    StudentsVO students2StudentsVO(Students students,Students2 students2);


    @Mappings({
            @Mapping(source = "students.name", target = "name"),
            @Mapping(source = "students.address", target = "addr"),
            @Mapping(source = "param", target = "ids")
    })
    StudentsVO students2StudentsVO(Students students, List<String> param);
}

4.验证

  Students students = new Students("一安未来3",0,"北京",Arrays.asList(1, 2, 3));
  StudentsVO studentsVO3 = StudentMapper.INSTANCE.students2StudentsVO(students);
  System.out.println("MapStruct:"+studentsVO3);
  
  //运行
  MapStruct:StudentsVO(name=一安未来3, gender=Y, addr=北京, ids=[1, 2, 3])

总结

Mapping源码中还有很多属性,比如支持日期格式化dateFormat,数字格式化numberFormat等操作

public @interface Mapping {
    String target();

    String source() default "";

    String dateFormat() default "";

    String numberFormat() default "";

    String constant() default "";

    String expression() default "";

    boolean ignore() default false;

    Class<? extends Annotation>[] qualifiedBy() default {};

    String[] qualifiedByName() default {};

    Class<?> resultType() default void.class;

    String[] dependsOn() default {};

    String defaultValue() default "";
}

号外!号外!

如果这篇文章对你有所帮助,或者有所启发的话,帮忙点赞、在看、转发、收藏,你的支持就是我坚持下去的最大动力!

MapStruct 高效-安全-优雅实现Bean转换

如何快速搭建一套生产级RabbitMQ


如何快速搭建一套生产级Elasticsearch


如何快速搭建一套生产级Redis


MapStruct 高效-安全-优雅实现Bean转换


原文始发于微信公众号(一安未来):MapStruct 高效-安全-优雅实现Bean转换

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/44855.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!