詳解Java對象轉(zhuǎn)換神器MapStruct庫的使用
前言
在我們?nèi)粘i_發(fā)的程序中,為了各層之間解耦,一般會定義不同的對象用來在不同層之間傳遞數(shù)據(jù),比如xxxDTO、xxxVO、xxxQO,當(dāng)在不同層之間傳輸數(shù)據(jù)時,不可避免地經(jīng)常需要將這些對象進行相互轉(zhuǎn)換。
今天給大家介紹一個對象轉(zhuǎn)換工具MapStruct,代碼簡潔安全、性能高,強烈推薦。
MapStruct簡介
MapStruct是一個代碼生成器,它基于約定優(yōu)于配置,極大地簡化了Java Bean類型之間映射的實現(xiàn)。特點如下:
- 基于注解
- 在編譯期自動生成映射轉(zhuǎn)換代碼
- 類型安全、高性能、無依賴性、易于理解閱讀
MapStruct入門
1. 引入依賴
這里使用Gradle構(gòu)建
dependencies?{ ????implementation?'org.mapstruct:mapstruct:1.4.2.Final' ????annotationProcessor?'org.mapstruct:mapstruct-processor:1.4.2.Final' }
2. 需要轉(zhuǎn)換的對象
創(chuàng)建兩個示例對象(e.g. 將Demo對象轉(zhuǎn)換為DemoDto對象)
/** ?*?源對象 ?*/ @Data public?class?Demo?{ ????private?Integer?id; ????private?String?name; } /** ?*?目標(biāo)對象 ?*/ @Data public?class?DemoDto?{ ????private?Integer?id; ????private?String?name; }
3. 創(chuàng)建轉(zhuǎn)換器
只需要創(chuàng)建一個轉(zhuǎn)換器接口類,并在類上添加 @Mapper 注解即可(官方示例推薦以 xxxMapper 格式命名轉(zhuǎn)換器名稱)
@Mapper public?interface?DemoMapper?{ ????//使用Mappers工廠獲取DemoMapper實現(xiàn)類 ????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class); ????//定義接口方法,參數(shù)為來源對象,返回值為目標(biāo)對象 ????DemoDto?toDemoDto(Demo?demo); }
4. 驗證
public?static?void?main(String[]?args)?{ ????Demo?demo?=?new?Demo(); ????demo.setId(111); ????demo.setName("hello"); ????DemoDto?demoDto?=?DemoMapper.INSTANCE.toDemoDto(demo); ????System.out.println("目標(biāo)對象demoDto為:"?+?demoDto); ????//輸出結(jié)果:目標(biāo)對象demoDto為:DemoDto(id=111, name=hello) }
測試結(jié)果如下:
目標(biāo)對象demoDto為:DemoDto(id=111, name=hello)
達到了我們的預(yù)期結(jié)果。
5. 自動生成的實現(xiàn)類
為什么聲明一個接口就可以轉(zhuǎn)換對象呢?我們看一下MapStruct在編譯期間自動生成的實現(xiàn)類:
@Generated( ????value?=?"org.mapstruct.ap.MappingProcessor", ????date?=?"2022-09-01T17:54:38+0800", ????comments?=?"version:?1.4.2.Final,?compiler:?IncrementalProcessingEnvironment?from?gradle-language-java-7.3.jar,?environment:?Java?1.8.0_231?(Oracle?Corporation)" ) public?class?DemoMapperImpl?implements?DemoMapper?{ ????@Override ????public?DemoDto?toDemoDto(Demo?demo)?{ ????????if?(?demo?==?null?)?{ ????????????return?null; ????????} ????????DemoDto?demoDto?=?new?DemoDto(); ????????demoDto.setId(?demo.getId()?); ????????demoDto.setName(?demo.getName()?); ????????return?demoDto; ????} }
可以看到,MapStruct幫我們將繁雜的代碼自動生成了,而且實現(xiàn)類中用的都是最基本的get、set方法,易于閱讀理解,轉(zhuǎn)換速度非???。
MapStruct進階
上面的例子只是小試牛刀,下面開始展示MapStruct的強大之處。
(限于篇幅,這里不展示自動生成的實現(xiàn)類和驗證結(jié)果,大家可自行測試)
場景1:屬性名稱不同、(基本)類型不同
- 屬性名稱不同: 在方法上加上 @Mapping 注解,用來映射屬性
- 屬性基本類型不同: 基本類型和String等類型會自動轉(zhuǎn)換
關(guān)鍵字:@Mapping注解
/** ?*?來源對象 ?*/ @Data public?class?Demo?{ ????private?Integer?id; ????private?String?name; } /** ?*?目標(biāo)對象 ?*/ @Data public?class?DemoDto?{ ????private?String?id; ????private?String?fullname; } /** ?*?轉(zhuǎn)換器 ?*/ @Mapper public?interface?DemoMapper?{ ????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class); ????@Mapping(target?=?"fullname",?source?=?"name") ????DemoDto?toDemoDto(Demo?demo); }
場景2:統(tǒng)一映射不同類型
下面例子中,time1、time2、time3都會被轉(zhuǎn)換,具體說明看下面的注釋:
/** ?*?來源對象 ?*/ @Data public?class?Demo?{ ????private?Integer?id; ????private?String?name; ????/** ?????*?time1、time2名稱相同,time3轉(zhuǎn)為time33 ?????*?這里的time1、time2、time33都是Date類型 ?????*/ ????private?Date?time1; ????private?Date?time2; ????private?Date?time3; } /** ?*?目標(biāo)對象 ?*/ @Data public?class?DemoDto?{ ????private?String?id; ????private?String?name; ????/** ?????*?這里的time1、time2、time33都是String類型 ?????*/ ????private?String?time1; ????private?String?time2; ????private?String?time33; } /** ?*?轉(zhuǎn)換器 ?*/ @Mapper public?interface?DemoMapper?{ ????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class); ????@Mapping(target?=?"time33",?source?=?"time3") ????DemoDto?toDemoDto(Demo?demo); ???? ????//MapStruct會將所有匹配到的: ????//源類型為Date、目標(biāo)類型為String的屬性, ????//按以下方法進行轉(zhuǎn)換 ????static?String?date2String(Date?date)?{ ????????SimpleDateFormat?simpleDateFormat?=?new?SimpleDateFormat("yyyy-MM-dd?HH:mm:ss"); ????????String?strDate?=?simpleDateFormat.format(date); ????????return?strDate; ????} }
場景3:固定值、忽略某個屬性、時間轉(zhuǎn)字符串格式
一個例子演示三種用法,具體說明看注釋,很容易理解:
關(guān)鍵字:ignore、constant、dateFormat
/** ?*?來源對象 ?*/ @Data public?class?Demo?{ ????private?Integer?id; ????private?String?name; ????private?Date?time; } /** ?*?目標(biāo)對象 ?*/ @Data public?class?DemoDto?{ ????private?String?id; ????private?String?name; ????private?String?time; } /** ?*?轉(zhuǎn)換器 ?*/ @Mapper public?interface?DemoMapper?{ ????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class); ????//id屬性不賦值 ????@Mapping(target?=?"id",?ignore?=?true) ????//name屬性固定賦值為“hello” ????@Mapping(target?=?"name",?constant?=?"hello") ????//time屬性轉(zhuǎn)為yyyy-MM-dd?HH:mm:ss格式的字符串 ????@Mapping(target?=?"time",?dateFormat?=?"yyyy-MM-dd?HH:mm:ss") ????DemoDto?toDemoDto(Demo?demo); }
場景4:為某個屬性指定轉(zhuǎn)換方法
場景2中,我們是按照某個轉(zhuǎn)換方法,統(tǒng)一將一種類型轉(zhuǎn)換為另外一種類型;而下面這個例子,是為某個屬性指定方法:
關(guān)鍵字:@Named注解、qualifiedByName
/** ?*?來源對象 ?*/ @Data public?class?Demo?{ ????private?Integer?id; ????private?String?name; } /** ?*?目標(biāo)對象 ?*/ @Data public?class?DemoDto?{ ????private?String?id; ????private?String?name; } /** ?*?轉(zhuǎn)換器 ?*/ @Mapper public?interface?DemoMapper?{ ????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class); ????//為name屬性指定@Named為convertName的方法進行轉(zhuǎn)換 ????@Mapping(target?=?"name",?qualifiedByName?=?"convertName") ????DemoDto?toDemoDto(Demo?demo); ????@Named("convertName") ????static?String?aaa(String?name)?{ ????????return?"姓名為:"?+?name; ????} }
場景5:多個參數(shù)合并為一個對象
如果參數(shù)為多個的話,@Mapping注解中的source就要指定是哪個參數(shù)了,用點分隔:
關(guān)鍵字:點(.)
/** ?*?來源對象 ?*/ @Data public?class?Demo?{ ????private?Integer?id; ????private?String?name; } /** ?*?目標(biāo)對象 ?*/ @Data public?class?DemoDto?{ ????private?String?fullname; ????private?String?timestamp; } /** ?*?轉(zhuǎn)換器 ?*/ @Mapper public?interface?DemoMapper?{ ????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class); ????//fullname屬性賦值demo對象的name屬性(注意這里.的用法) ????//timestamp屬性賦值為傳入的time參數(shù) ????@Mapping(target?=?"fullname",?source?=?"demo.name") ????@Mapping(target?=?"timestamp",?source?=?"time") ????DemoDto?toDemoDto(Demo?demo,?String?time); }
場景6:已有目標(biāo)對象,將源對象屬性覆蓋到目標(biāo)對象
覆蓋目標(biāo)對象屬性時,一般null值不覆蓋,所以需要在類上的@Mapper注解中添加屬性:nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE 代表null值不進行賦值。
關(guān)鍵字:@MappingTarget注解、nullValuePropertyMappingStrategy
/** ?*?來源對象 ?*/ @Data public?class?Demo?{ ????private?Integer?id; ????private?String?name; } /** ?*?目標(biāo)對象 ?*/ @Data public?class?DemoDto?{ ????private?String?id; ????private?String?name; } /** ?*?轉(zhuǎn)換器 ?*/ @Mapper(unmappedTargetPolicy?=?ReportingPolicy.IGNORE, ????????nullValuePropertyMappingStrategy?=?NullValuePropertyMappingStrategy.IGNORE) public?interface?DemoMapper?{ ????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class); ????//將已有的目標(biāo)對象當(dāng)作一個參數(shù)傳進來 ????DemoDto?toDemoDto(Demo?demo,?@MappingTarget?DemoDto?dto); }
場景7:源對象兩個屬性合并為一個屬性
這種情況可以使用@AfterMapping注解。
關(guān)鍵字:@AfterMapping注解、@MappingTarget注解
/** ?*?來源對象 ?*/ @Data public?class?Demo?{ ????private?Integer?id; ????private?String?firstName; ????private?String?lastName; } /** ?*?目標(biāo)對象 ?*/ @Data public?class?DemoDto?{ ????private?String?id; ????private?String?name; } /** ?*?轉(zhuǎn)換器 ?*/ @Mapper public?interface?DemoMapper?{ ????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class); ????DemoDto?toDemoDto(Demo?demo); ????//在轉(zhuǎn)換完成后執(zhí)行的方法,一般用到源對象兩個屬性合并為一個屬性的場景 ????//需要將源對象、目標(biāo)對象(@MappingTarget)都作為參數(shù)傳進來, ????@AfterMapping ????static?void?afterToDemoDto(Demo?demo,?@MappingTarget?DemoDto?demoDto)?{ ????????String?name?=?demo.getFirstName()?+?demo.getLastName(); ????????demoDto.setName(name); ????} }
小結(jié)
本文介紹了對象轉(zhuǎn)換工具 MapStruct 庫,以安全、簡潔、優(yōu)雅的方式來優(yōu)化我們的轉(zhuǎn)換代碼。
從文中的示例場景中可以看出,MapStruct 提供了大量的功能和配置,使我們可以快捷的創(chuàng)建出各種或簡單或復(fù)雜的映射器。而這些,也只是 MapStruct 庫的冰山一角,還有很多強大的功能文中沒有提到,感興趣的朋友可以自行查看官方文檔。
到此這篇關(guān)于詳解Java對象轉(zhuǎn)換神器MapStruct庫的使用的文章就介紹到這了,更多相關(guān)Java MapStruct內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Spring中@Valid和@Validated注解用法
本文將以新增一個員工為功能切入點,以常規(guī)寫法為背景,慢慢烘托出?@Valid?和?@Validated?注解用法詳解,文中的示例代碼講解詳細,感興趣的可以了解一下2022-07-07springboot+vue實現(xiàn)websocket配置過程解析
這篇文章主要介紹了springboot+vue實現(xiàn)websocket配置過程解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-04-04Maven中dependency和plugins的繼承與約束
這篇文章主要介紹了Maven中dependency和plugins的繼承與約束,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12kafka生產(chǎn)者和消費者的javaAPI的示例代碼
這篇文章主要介紹了kafka生產(chǎn)者和消費者的javaAPI的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06SpringBoot3整合Druid監(jiān)控功能的項目實踐
Druid連接池作為一款強大的數(shù)據(jù)庫連接池,提供了豐富的監(jiān)控和管理功能,成為很多Java項目的首選,本文主要介紹了SpringBoot3整合Druid監(jiān)控功能的項目實踐,感興趣的可以了解一下2024-01-01