您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

使用MapStruct,让Bean对象之间转换更简单

时间:2022-02-23 10:46:17  来源:  作者:小黑说Java

场景

通常,在后端项目开发中,因为会有项目分层的设计,例如MVC架构,以及最近很火热的DDD架构中,会在不同的层级,有对应的DO,BO,VO,DTO等各种各样的POJO类,而我们在层级之间进行调用的数据传递时,通常要进行对象属性之间的映射。对于一些简单的对象,可能会直接使用get,set方法完成,或者使用BeanUtils工具类来完成属性之间的映射。

这些代码往往是枯燥、无聊的,并且在不同的业务处理类中可能需要重复地对两个对象进行互相转换。导致代码里充斥着大量的get,set转换,如果使用BeanUtils,可能会因为字段名称不一致,导致在运行时才能发现问题。

那有没有什么方案能解决这个问题呢?

答案就是使用MapStruct,可以优雅地解决上面的这些问题。

MapStruct是一种代码生成器组件,它遵循约定优于配置的原则,可以让我们的Bean对象之间的转换变得更简单。

为什么要使用MapStruct?

如前文中描述,在多层应用设计中,需要在不同的对象模型之间进行转换,属性映射,手动编写这些代码不仅繁琐,而且很容易出错,MapStruct的目的是让这项工作变得简单,自动化。

相比其他的映射框架,比如BeanUtils,或者Json序列化反序列化等方式,MapStruct能在编译时就生成映射,确保程序运行性能,并且能在编译时就发现错误。

MapStruct怎么用?

MapStruct本质上是一个注解处理器,可以直接在Maven或Gradle等编译工具中集成。

以Maven为例,我们需要先在依赖中添加MapStruct依赖,并将mapstruct-processor配置在maven插件中。

<properties>
    <org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
</properties>

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

<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>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>${org.mapstruct.version}</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

接下来,便可以在代码中使用MapStruct。

同名字段映射

比如,我们现在有一个功能是要从持久层查询学生对象Student,然后将它转换为StudentDTO传递给业务层。这里我们需要在Student对象和StudentDTO对象之间进行转换。

Student.JAVA

@Data
public class Student{
 private int no;
 private String name;
}

StudentDTO.java

@Data
public class StudentDTO {
 private int no;
 private String name;
}

针对这种对象之间的转换,我们需要创建一个MApper类进行映射。

@Mapper(componentModel = "spring")
public interface StudentMapper {
    StudentDTO toDto(Student student);
}

@Mapper :是MapStruct的注解,用于创建和生成映射的实现。

componentModel = "spring":该属性的含义是将StudentMapper的实例作为Spring的Bean对象,放在Spring容器中,这样就可以在其他业务代码中方便的注入。

因为Student和StudentDTO的属性名相同,所以我们不需要任何其他代码显式映射。

不同名字段映射

假如DTO和PO之间的字段名称不同,应该如何处理呢?

Student.java

@Data
public class Student{
    private int no;
    private String name;
    private String gender;
}

StudentDTO.java

@Data
public class StudentDTO {
    private int no;
    private String name;
    private String sex;
}

如上代码所示,在Student和StudentDTO中,性别字段的名称不一致。要实现这种情况的映射,只需要添加如下的@Mapping注解。

@Mapper(componentModel = "spring")
public interface StudentMapper {
     @Mapping(source = "gender", target = "sex")
    StudentDTO toDto(Student student);
}

自定义对象属性映射

假如每个学生有自己的地址信息Address,那么该如何处理呢?

源对象类

@Data
public class Student{
    private int no;
    private String name;
    private String gender;
    private Address address;
}

@Data
public class Address{
    private String city;
    private String province;
}

目标对象类

@Data
public class StudentDTO{
    private int no;
    private String name;
    private String sex;
    private AddressDTO address;
}

@Data
public class AddressDTO{
    private String city;
    private String province;
}

这种要对内部对象进行映射,我们需要对内部对象也创建一个Mapper,然后将内部对象的Mapper导入到StudentMapper中。

@Mapper(componentModel = "spring")
public interface AddressMapper {
    AddressDTO toDto(Address address);
}

@Mapper(componentModel = "spring",uses = {AddressMapper.class})
public interface StudentMapper {
    @Mapping(source = "gender", target = "sex")
    StudentDTO toDto(Student student);
}

自定义转换逻辑映射

如果在对象转换时,不仅是简单的属性之间的映射,还需要按照某种业务逻辑进行转换,比如每个Student中的地址信息Address,在StudentDTO中只需要地址信息中的city。

源对象类

@Data
public class Student{
    private int no;
    private String name;
    private String gender;
    private Address address;
}

@Data
public class Address{
    private String city;
    private String province;
}

目标对象类

@Data
public class StudentDTO{
    private int no;
    private String name;
    private String sex;
    private String city;
}

针对这种情况,我们可以直接在source中使用address.city,也可以通过自定义方法来完成逻辑转换。

@Mapper(componentModel = "spring",uses = {AddressMapper.class})
public interface StudentMapper {
    @Mapping(source = "gender", target = "sex")
    // @Mapping(source = "address.city", target = "city")
    @Mapping(source = "address", target = "city",qualifiedByName = "getAddressCity")
    StudentDTO toDto(Student student);

    @Named("getAddressCity")
    default String getChildCircuits(Address address) {
        if(address == null) {
            return "";
        }
        return address.getCity();
    }
}

集合映射

使用MapStruct进行集合字段的映射也很简单。比如每个Student中有多门选修的课程List<Course>,要映射到StudentDTO中的List<CourseDTO>中。

@Mapper(componentModel = "spring")
public interface CourseMapper {
    CourseDTO toDto(Course port);

    List<CourseDTO> toCourseDtoList(List<Course> courses);
}

@Mapper(componentModel = "spring",uses = {CourseMapper.class})
public interface StudentMapper {
    @Mapping(source = "gender", target = "sex")
    @Mapping(source = "address", target = "city",qualifiedByName = "getAddressCity")
    CircuitDto toDto(Circuit circuit);
}

其他特殊情况映射

除了常见的对象映射外,有些情况我们可能需要在转换时设置一些固定值,假设StudentDTO中有学历字段degree,但是暂时该数据还未录入,所以这里要设置默认值“未知”。可以使用@Mapping注解完成。

@Mapping(target = "degree", constant = "未知")

@BeforeMapping和@AfterMapping

@BeforeMapping和@AfterMapping是两个很重要的注解,看名字基本就可以猜到,可以用来在转换之前和之后做一些处理。

比如我们想要在转换之前做一些数据验证,集合初始化等功能,可以使用@BeforeMapping;

想要在转换完成之后进行一些数据结果的修改,比如根据更加StudentDTO中选修课程List<CourseDTO>的数量来给是否有选修课字段haveCourse设置布尔值等。

@Mapper(componentModel = "spring",uses = {PortMapper.class})
public interface StudentMapper {
    @BeforeMapping
    default void setCourses(Student student) {
        if(student.getCourses() == null){
            student.setCourses(new ArrayList<Course>());
        }
    }

    @Mapping(source = "gender", target = "sex")
    StudentDTO toDto(Student student);

    @AfterMapping
    default void setHaveCourse(StudentDTO studentDto) {
        if(studentDto.getCourses()!=null && studentDto.getCourses() >0){
            studentDto.setHaveCourse(true);
        }
    } 
}

总结

在本文中介绍了如何使用MapStruct来优雅的实现对象属性映射,减少我们代码中的get,set代码,避免对象属性之间映射的错误。在以上示例代码中可以看出,MapStruct大量使用了注解,让我们可以轻松完成对象映射。

如果你想了解更多MapStruct相关信息,可以继续阅读官方的文档。MapStruct官方文档



Tags:MapStruct   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
使用 Mapstructure 解析 Json,你学会了吗?
背景前几天群里的小伙伴问了一个这样的问题:图片其实质就是在面对 value 类型不确定的情况下,怎么解析这个 json?我下意识就想到了 [mapstructure](https://github.com/mitchel...【详细内容】
2023-12-27  Search: MapStruct  点击:(159)  评论:(0)  加入收藏
重磅,MapStruct 1.5 发布,这次终于支持Map转为Bean
本月三号,MapStruct 1.5.0 Final发布,本次正式版距离上次正式版发布已经过去了快7年(上个正式版发布于2015年11月),此次发布除了修复了110多个bug外,还有以下新特性值得关注: 支持...【详细内容】
2022-06-21  Search: MapStruct  点击:(626)  评论:(0)  加入收藏
使用MapStruct,让Bean对象之间转换更简单
场景通常,在后端项目开发中,因为会有项目分层的设计,例如MVC架构,以及最近很火热的DDD架构中,会在不同的层级,有对应的DO,BO,VO,DTO等各种各样的POJO类,而我们在层级之间进行调用的数...【详细内容】
2022-02-23  Search: MapStruct  点击:(479)  评论:(0)  加入收藏
▌简易百科推荐
Java 8 内存管理原理解析及内存故障排查实践
本文介绍Java8虚拟机的内存区域划分、内存垃圾回收工作原理解析、虚拟机内存分配配置,以及各垃圾收集器优缺点及场景应用、实践内存故障场景排查诊断,方便读者面临内存故障时...【详细内容】
2024-03-20  vivo互联网技术    Tags:Java 8   点击:(17)  评论:(0)  加入收藏
如何编写高性能的Java代码
作者 | 波哥审校 | 重楼在当今软件开发领域,编写高性能的Java代码是至关重要的。Java作为一种流行的编程语言,拥有强大的生态系统和丰富的工具链,但是要写出性能优异的Java代码...【详细内容】
2024-03-20    51CTO  Tags:Java代码   点击:(25)  评论:(0)  加入收藏
在Java应用程序中释放峰值性能:配置文件引导优化(PGO)概述
译者 | 李睿审校 | 重楼在Java开发领域,优化应用程序的性能是开发人员的持续追求。配置文件引导优化(Profile-Guided Optimization,PGO)是一种功能强大的技术,能够显著地提高Ja...【详细内容】
2024-03-18    51CTO  Tags:Java   点击:(29)  评论:(0)  加入收藏
Java生产环境下性能监控与调优详解
堆是 JVM 内存中最大的一块内存空间,该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中。堆被划分为新生代和老年代,新生代又被进一步划分为 Eden 和 Survivor 区,...【详细内容】
2024-02-04  大雷家吃饭    Tags:Java   点击:(60)  评论:(0)  加入收藏
在项目中如何避免和解决Java内存泄漏问题
在Java中,内存泄漏通常指的是程序中存在一些不再使用的对象或数据结构仍然保持对内存的引用,从而导致这些对象无法被垃圾回收器回收,最终导致内存占用不断增加,进而影响程序的性...【详细内容】
2024-02-01  编程技术汇  今日头条  Tags:Java   点击:(75)  评论:(0)  加入收藏
Java中的缓存技术及其使用场景
Java中的缓存技术是一种优化手段,用于提高应用程序的性能和响应速度。缓存技术通过将计算结果或者经常访问的数据存储在快速访问的存储介质中,以便下次需要时可以更快地获取。...【详细内容】
2024-01-30  编程技术汇    Tags:Java   点击:(75)  评论:(0)  加入收藏
JDK17 与 JDK11 特性差异浅谈
从 JDK11 到 JDK17 ,Java 的发展经历了一系列重要的里程碑。其中最重要的是 JDK17 的发布,这是一个长期支持(LTS)版本,它将获得长期的更新和支持,有助于保持程序的稳定性和可靠性...【详细内容】
2024-01-26  政采云技术  51CTO  Tags:JDK17   点击:(95)  评论:(0)  加入收藏
Java并发编程高阶技术
随着计算机硬件的发展,多核处理器的普及和内存容量的增加,利用多线程实现异步并发成为提升程序性能的重要途径。在Java中,多线程的使用能够更好地发挥硬件资源,提高程序的响应...【详细内容】
2024-01-19  大雷家吃饭    Tags:Java   点击:(111)  评论:(0)  加入收藏
这篇文章彻底让你了解Java与RPA
前段时间更新系统的时候,发现多了一个名为Power Automate的应用,打开了解后发现是一个自动化应用,根据其描述,可以自动执行所有日常任务,说的还是比较夸张,简单用了下,对于office、...【详细内容】
2024-01-17  Java技术指北  微信公众号  Tags:Java   点击:(102)  评论:(0)  加入收藏
Java 在 2023 年仍然流行的 25 个原因
译者 | 刘汪洋审校 | 重楼学习 Java 的过程中,我意识到在 90 年代末 OOP 正值鼎盛时期,Java 作为能够真正实现这些概念的语言显得尤为突出(尽管我此前学过 C++,但相比 Java 影响...【详细内容】
2024-01-10  刘汪洋  51CTO  Tags:Java   点击:(81)  评论:(0)  加入收藏
相关文章
    无相关信息
站内最新
站内热门
站内头条