⚡效率工具⚡ - 推薦一款對象映射神器「MapStruct」|8月更文挑戰

前言

工做中經常出現的一種狀況是,咱們須要把Entity/PO/DTO/VO/QueryParam之間作轉換,解決這類問題的工具備不少,如OrikaBeanUtilsHutool工具包,爲什麼對MapStrucet情有獨鍾,用來單獨推薦呢?html


簡介

MapSturct 是一個生成類型安全, 高性能且無依賴的 JavaBean 映射代碼的註解處理器java

怎麼理解呢,對於BeanUtils來講,映射主要是靠反射來實現,當有大量的拷貝時,意味着大量的使用了反射,效率相對低下,就連《阿里巴巴開發手冊》中也明確提到,不許使用BeanUtilsgit

衆所周知,效率最快的固然是手寫的get()、set(),固然開發效率也是最慢的,而MapStruct經過編譯器編譯生成常規的方法,咱們經過寫接口和註解就能夠手動幫咱們生成get()、set()代碼,效率不知提升了多少倍github

具體性能測試已經有人作過了,能夠參考這篇文章:5種常見Bean映射工具的性能比對spring


示例

首先引入MapStruct的依賴apache

Maven:安全

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.4.2.Final</version>
</dependency>
複製代碼

在Plugins加上:markdown

<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>1.4.2.Final</version>
            </path>
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>
複製代碼

現有兩個實體類app

@Data
public class Student {
    String name;
    Integer age;
    String idCard;
    Date birthDay;
}
複製代碼
@Data
public class StudentVO {
    String name;
    String birthDay;
    String idCard;
    Integer studentAge;
}
複製代碼

場景1:單個對象之間的映射 / 批量映射

按照需求,咱們要把Student對象轉換爲StudentVO對象,其中student.age 要映射到studentVO.studentAge中,而 student.idCard在此次查詢中無需顯示,student.birthDay格式化成字符串再傳入studentVO.bidthDay框架

咱們只須要建立一個接口:

@Component
@Mapper(componentModel = "spring")
public interface StudentConverter {
    
    @Mapping(target = "studentAge", source = "age")
    @Mapping(target = "idCard", ignore = true)
    @Mapping(target = "birthDay", dateFormat = "yyyy-MM-dd HH:mm:ss")
    StudentVO studentToStudentVO(Student student);
    
    List<StudentVO> studentToStudentVO(List<Student> students);
}
複製代碼

使用:

@RestController
public class TestController {

    @Autowired
    private StudentConverter studentConverter;

    @GetMapping("/test")
    public void beanConvertTest() {
        Student student = new Student();
        student.setName("name");
        student.setAge(18);
        student.setIdCard("123456");
        student.setBirthDay(new Date());

        StudentVO studentVO = studentConverter.studentToStudentVO(student);
        System.out.println(studentVO);

        List<Student> students = Collections.singletonList(student);
        List<StudentVO> studentVOS = studentConverter.studentToStudentVO(students);
        System.out.println(studentVOS);
    }
}
複製代碼

輸出結果:

能夠看到出生日期被成功被格式化成咱們想要的字符串,idCard咱們不須要因而沒有被映射


場景2:多個對象映射爲一個

有時候會出現要將多個對象映射爲一個對象的狀況,而多個對象之間可能有重複的字段,這個根據@Mapping註解就能靈活解決

如今新增一個類Address.class,其中name字段和student.name是重複字段

@Data
public class Address {
    String name;
    String address;
}
複製代碼

需求是在場景1的基礎上,將address字段加到StudentVO.class中,可是name字段得用Student.class 的。

@Data
public class StudentVO {
    String name;
    String birthDay;
    String idCard;
    Integer studentAge;
    //新增字段
    String address;
}
複製代碼

咱們能夠經過source參數來設置須要映射的字段來自於哪一個實例,接口方法以下:

@Mapping(target = "studentAge", source = "student.age")
@Mapping(target = "idCard", ignore = true)
@Mapping(target = "birthDay", source = "student.birthDay", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Mapping(target = "name", source = "student.name")
StudentVO studentAndAddressToVO(Student student, Address address);
複製代碼

使用:

@GetMapping("/test")
public void beanConvertTest() {
    Student student = new Student();
    student.setName("name");
    student.setAge(18);
    student.setIdCard("123456");
    student.setBirthDay(new Date());

    Address address = new Address();
    address.setName("addressName");
    address.setAddress("address");

    StudentVO studentVO = studentConverter.studentAndAddressToVO(student, address);
    System.out.println(studentVO);
}
複製代碼

輸出結果:

對於上面的例子,須要注意的是,多個對象映射爲一個對象時,只有重複的字段,或者字段名不同的字段進行映射,才須要用註解告訴MapStruct,到底要使用哪一個來源的字段

更多更復雜的用法能夠參考官方示例倉庫,很是齊全:mapstruct/mapstruct-examples: Examples for using MapStruct (github.com)


其餘注意事項

  • 若是項目中也同時使用到了 Lombok,必定要注意 Lombok的版本要等於或者高於1.18.10,不然會有編譯不經過的狀況發生
  • 若是接口的存放包名爲mapper,可能與Mybatis衝突會致使項目起不來
  • 當兩個對象屬性不一致時,好比Student對象中某個字段不存在於StudentVO當中時,在編譯時會有警告提示,能夠在@Mapping中配置 ignore = true,當字段較多時,能夠直接在@Mapper中設置unmappedTargetPolicy屬性或者unmappedSourcePolicy屬性爲 ReportingPolicy.IGNORE便可

總結:應該如何選擇對象映射工具

  1. 如果字段少,寫起來也不麻煩,就不必用框架了,手寫get()/set() 就行了,技術不該該爲用而用,應該爲了解決對應的問題而用
  2. 如果字段多,轉換不頻繁,爲省事就用BeanUtils吧,稍微複雜點的場景也可使用 Hutool工具包BeanUtils,提供的拷貝選項要多一些
  3. 字段多又轉換頻繁,從性能方面考慮,仍是從高性能的工具中選一個用吧,好比本文推薦的MapStruct

Reference

經常使用開發庫 - MapStruct工具庫詳解 | Java 全棧知識體系 (pdai.tech)

丟棄掉那些BeanUtils工具類吧,MapStruct真香!!! (juejin.cn)

相關文章
相關標籤/搜索