工做中經常出現的一種狀況是,咱們須要把Entity/PO/DTO/VO/QueryParam之間作轉換,解決這類問題的工具備不少,如Orika、BeanUtils、Hutool工具包,爲什麼對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;
}
複製代碼
按照需求,咱們要把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
咱們不須要因而沒有被映射
有時候會出現要將多個對象映射爲一個對象的狀況,而多個對象之間可能有重複的字段,這個根據@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)
Student
對象中某個字段不存在於StudentVO
當中時,在編譯時會有警告提示,能夠在@Mapping
中配置 ignore = true
,當字段較多時,能夠直接在@Mapper
中設置unmappedTargetPolicy
屬性或者unmappedSourcePolicy
屬性爲 ReportingPolicy.IGNORE
便可get()/set()
就行了,技術不該該爲用而用,應該爲了解決對應的問題而用BeanUtils
吧,稍微複雜點的場景也可使用 Hutool工具包的BeanUtils
,提供的拷貝選項要多一些