您好,登錄后才能下訂單哦!
本篇內容主要講解“為什么使用MapStruct工具類”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“為什么使用MapStruct工具類”吧!
為什么需要MapStruct ?
首先,我們先說一下MapStruct這類框架適用于什么樣的場景,為什么市面上會有這么多的類似的框架。
在軟件體系架構設計中,分層式結構是最常見,也是最重要的一種結構。很多人都對三層架構、四層架構等并不陌生。
甚至有人說:"計算機科學領域的任何問題都可以通過增加一個間接的中間層來解決,如果不行,那就加兩層。"
但是,隨著軟件架構分層越來越多,那么各個層次之間的數據模型就要面臨著相互轉換的問題,典型的就是我們可以在代碼中見到各種O,如DO、DTO、VO等。
一般情況下,同樣一個數據模型,我們在不同的層次要使用不同的數據模型。如在數據存儲層,我們使用DO來抽象一個業務實體;在業務邏輯層,我們使用DTO來表示數據傳輸對象;到了展示層,我們又把對象封裝成VO來與前端進行交互。
那么,數據的從前端透傳到數據持久化層(從持久層透傳到前端),就需要進行對象之間的互相轉化,即在不同的對象模型之間進行映射。
通常我們可以使用get/set等方式逐一進行字段映射操作,如:
personDTO.setName(personDO.getName()); personDTO.setAge(personDO.getAge()); personDTO.setSex(personDO.getSex()); personDTO.setBirthday(personDO.getBirthday());
但是,編寫這樣的映射代碼是一項冗長且容易出錯的任務。MapStruct等類似的框架的目標是通過自動化的方式盡可能多地簡化這項工作。
MapStruct的使用
MapStruct是一種代碼生成器,它極大地簡化了基于"約定優于配置"方法的Java bean類型之間映射的實現。生成的映射代碼使用純方法調用,因此快速、類型安全且易于理解。
約定優于配置,也稱作按約定編程,是一種軟件設計范式,旨在減少軟件開發人員需做決定的數量,獲得簡單的好處,而又不失靈活性。
假設我們有兩個類需要進行互相轉換,分別是PersonDO和PersonDTO,類定義如下:
public class PersonDO { private Integer id; private String name; private int age; private Date birthday; private String gender; } public class PersonDTO { private String userName; private Integer age; private Date birthday; private Gender gender; }
我們演示下如何使用MapStruct進行bean映射。
想要使用MapStruct,首先需要依賴他的相關的jar包,使用maven依賴方式如下:
... <properties> <org.mapstruct.version>1.3.1.Final</org.mapstruct.version> </properties> ... <dependencies> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version> </dependency> </dependencies> ... <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <!-- depending on your project --> <target>1.8</target> <!-- depending on your project --> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> <!-- other annotation processors --> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>
因為MapStruct需要在編譯器生成轉換代碼,所以需要在maven-compiler-plugin插件中配置上對mapstruct-processor的引用。這部分在后文會再次介紹。
之后,我們需要定義一個做映射的接口,主要代碼如下:
@Mapper interface PersonConverter { PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class); @Mappings(@Mapping(source = "name", target = "userName")) PersonDTO do2dto(PersonDO person); }
使用注解@Mapper定義一個Converter接口,在其中定義一個do2dto方法,方法的入參類型是PersonDO,出參類型是PersonDTO,這個方法就用于將PersonDO轉成PersonDTO。
測試代碼如下:
public static void main(String[] args) { PersonDO personDO = new PersonDO(); personDO.setName("Hollis"); personDO.setAge(26); personDO.setBirthday(new Date()); personDO.setId(1); personDO.setGender(Gender.MALE.name()); PersonDTO personDTO = PersonConverter.INSTANCE.do2dto(personDO); System.out.println(personDTO); }
輸出結果:
PersonDTO{userName='Hollis', age=26, birthday=Sat Aug 08 19:00:44 CST 2020, gender=MALE}
可以看到,我們使用MapStruct完美的將PersonDO轉成了PersonDTO。
上面的代碼可以看出,MapStruct的用法比較簡單,主要依賴@Mapper注解。
但是我們知道,大多數情況下,我們需要互相轉換的兩個類之間的屬性名稱、類型等并不完全一致,還有些情況我們并不想直接做映射,那么該如何處理呢?
其實MapStruct在這方面也是做的很好的。
MapStruct處理字段映射
首先,可以明確的告訴大家,如果要轉換的兩個類中源對象屬性與目標對象屬性的類型和名字一致的時候,會自動映射對應屬性。
那么,如果遇到特殊情況如何處理呢?
名字不一致如何映射
如上面的例子中,在PersonDO中用name表示用戶名稱,而在PersonDTO中使用userName表示用戶名,那么如何進行參數映射呢。
這時候就要使用@Mapping注解了,只需要在方法簽名上,使用該注解,并指明需要轉換的源對象的名字和目標對象的名字就可以了,如將name的值映射給userName,可以使用如下方式:
@Mapping(source = "name", target = "userName")
可以自動映射的類型
除了名字不一致以外,還有一種特殊情況,那就是類型不一致,如上面的例子中,在PersonDO中用String類型表示用戶性別,而在PersonDTO中使用一個Genter的枚舉表示用戶性別。
這時候類型不一致,就需要涉及到互相轉換的問題
其實,MapStruct會對部分類型自動做映射,不需要我們做額外配置,如例子中我們將String類型自動轉成了枚舉類型。
一般情況下,對于以下情況可以做自動類型轉換:
基本類型及其他們對應的包裝類型。
基本類型的包裝類型和String類型之間
String類型和枚舉類型之間
自定義常量
如果我們在轉換映射過程中,想要給一些屬性定義一個固定的值,這個時候可以使用 constant
@Mapping(source = "name", constant = "hollis")
類型不一致的如何映射
還是上面的例子,如果我們需要在Person這個對象中增加家庭住址這個屬性,那么我們一般在PersonoDTO中會單獨定義一個HomeAddress類來表示家庭住址,而在Person類中,我們一般使用String類型表示家庭住址。
這就需要在HomeAddress和String之間使用JSON進行互相轉化,這種情況下,MapStruct也是可以支持的。
public class PersonDO { private String name; private String address; } public class PersonDTO { private String userName; private HomeAddress address; } @Mapper interface PersonConverter { PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class); @Mapping(source = "userName", target = "name") @Mapping(target = "address",expression = "java(homeAddressToString(dto2do.getAddress()))") PersonDO dto2do(PersonDTO dto2do); default String homeAddressToString(HomeAddress address){ return JSON.toJSONString(address); } }
我們只需要在PersonConverter中在定義一個方法(因為PersonConverter是一個接口,所以在JDK 1.8以后的版本中可以定義一個default方法),這個方法的作用就是將HomeAddress轉換成String類型。
default方法:Java 8 引入的新的語言特性,用關鍵字default來標注,被default所標注的方法,需要提供實現,而子類可以選擇實現或者不實現該方法
然后在dto2do方法上,通過以下注解方式即可實現類型的轉換:
@Mapping(target = "address",expression = "java(homeAddressToString(dto2do.getAddress()))")
上面這種是自定義的類型轉換,還有一些類型的轉換是MapStruct本身就支持的,如String和Date之間的轉換:
@Mapping(target = "birthday",dateFormat = "yyyy-MM-dd HH:mm:ss")
以上,簡單介紹了一些常用的字段映射的方法,也是我自己在工作中經常遇到的幾個場景
MapStruct的性能
前面說了這么多MapStruct的用法,可以看出MapStruct的使用還是比較簡單的,并且字段映射上面的功能很強大,那么他的性能到底怎么樣呢?
分別執行1000、10000、100000、1000000次映射的耗時分別為:0ms、1ms、3ms、6ms。
可以看到,MapStruct的耗時相比較于其他幾款工具來說是非常短的。
那么,為什么MapStruct的性能可以這么好呢?
其實,MapStruct和其他幾類框架最大的區別就是:與其他映射框架相比,MapStruct在編譯時生成bean映射,這確保了高性能,可以提前將問題反饋出來,也使得開發人員可以徹底的錯誤檢查。
還記得前面我們在引入MapStruct的依賴的時候,特別在maven-compiler-plugin中增加了mapstruct-processor的支持嗎?
并且我們在代碼中使用了很多MapStruct提供的注解,這使得在編譯期,MapStruct就可以直接生成bean映射的代碼,相當于代替我們寫了很多setter和getter。
如我們在代碼中定義了以下一個Mapper:
@Mapper interface PersonConverter { PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class); @Mapping(source = "userName", target = "name") @Mapping(target = "address",expression = "java(homeAddressToString(dto2do.getAddress()))") @Mapping(target = "birthday",dateFormat = "yyyy-MM-dd HH:mm:ss") PersonDO dto2do(PersonDTO dto2do); default String homeAddressToString(HomeAddress address){ return JSON.toJSONString(address); } }
經過代碼編譯后,會自動生成一個PersonConverterImpl:
@Generated( value = "org.mapstruct.ap.MappingProcessor", date = "2020-08-09T12:58:41+0800", comments = "version: 1.3.1.Final, compiler: javac, environment: Java 1.8.0_181 (Oracle Corporation)" ) class PersonConverterImpl implements PersonConverter { @Override public PersonDO dto2do(PersonDTO dto2do) { if ( dto2do == null ) { return null; } PersonDO personDO = new PersonDO(); personDO.setName( dto2do.getUserName() ); if ( dto2do.getAge() != null ) { personDO.setAge( dto2do.getAge() ); } if ( dto2do.getGender() != null ) { personDO.setGender( dto2do.getGender().name() ); } personDO.setAddress( homeAddressToString(dto2do.getAddress()) ); return personDO; } }
在運行期,對于bean進行映射的時候,就會直接調用PersonConverterImpl的dto2do方法,這樣就沒有什么特殊的事情要做了,只是在內存中進行set和get就可以了。
所以,因為在編譯期做了很多事情,所以MapStruct在運行期的性能會很好,并且還有一個好處,那就是可以把問題的暴露提前到編譯期。
使得如果代碼中字段映射有問題,那么應用就會無法編譯,強制開發者要解決這個問題才行。
到此,相信大家對“為什么使用MapStruct工具類”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。