詳解Java對象轉換神器MapStruct庫的使用
前言
在我們日常開發(fā)的程序中,為了各層之間解耦,一般會定義不同的對象用來在不同層之間傳遞數據,比如xxxDTO、xxxVO、xxxQO,當在不同層之間傳輸數據時,不可避免地經常需要將這些對象進行相互轉換。
今天給大家介紹一個對象轉換工具MapStruct,代碼簡潔安全、性能高,強烈推薦。
MapStruct簡介
MapStruct是一個代碼生成器,它基于約定優(yōu)于配置,極大地簡化了Java Bean類型之間映射的實現。特點如下:
- 基于注解
- 在編譯期自動生成映射轉換代碼
- 類型安全、高性能、無依賴性、易于理解閱讀
MapStruct入門
1. 引入依賴
這里使用Gradle構建
dependencies?{
????implementation?'org.mapstruct:mapstruct:1.4.2.Final'
????annotationProcessor?'org.mapstruct:mapstruct-processor:1.4.2.Final'
}
2. 需要轉換的對象
創(chuàng)建兩個示例對象(e.g. 將Demo對象轉換為DemoDto對象)
/**
?*?源對象
?*/
@Data
public?class?Demo?{
????private?Integer?id;
????private?String?name;
}
/**
?*?目標對象
?*/
@Data
public?class?DemoDto?{
????private?Integer?id;
????private?String?name;
}
3. 創(chuàng)建轉換器
只需要創(chuàng)建一個轉換器接口類,并在類上添加 @Mapper 注解即可(官方示例推薦以 xxxMapper 格式命名轉換器名稱)
@Mapper
public?interface?DemoMapper?{
????//使用Mappers工廠獲取DemoMapper實現類
????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class);
????//定義接口方法,參數為來源對象,返回值為目標對象
????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("目標對象demoDto為:"?+?demoDto);
????//輸出結果:目標對象demoDto為:DemoDto(id=111, name=hello)
}
測試結果如下:
目標對象demoDto為:DemoDto(id=111, name=hello)
達到了我們的預期結果。
5. 自動生成的實現類
為什么聲明一個接口就可以轉換對象呢?我們看一下MapStruct在編譯期間自動生成的實現類:
@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幫我們將繁雜的代碼自動生成了,而且實現類中用的都是最基本的get、set方法,易于閱讀理解,轉換速度非??臁?/p>
MapStruct進階
上面的例子只是小試牛刀,下面開始展示MapStruct的強大之處。
(限于篇幅,這里不展示自動生成的實現類和驗證結果,大家可自行測試)
場景1:屬性名稱不同、(基本)類型不同
- 屬性名稱不同: 在方法上加上 @Mapping 注解,用來映射屬性
- 屬性基本類型不同: 基本類型和String等類型會自動轉換
關鍵字:@Mapping注解
/**
?*?來源對象
?*/
@Data
public?class?Demo?{
????private?Integer?id;
????private?String?name;
}
/**
?*?目標對象
?*/
@Data
public?class?DemoDto?{
????private?String?id;
????private?String?fullname;
}
/**
?*?轉換器
?*/
@Mapper
public?interface?DemoMapper?{
????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class);
????@Mapping(target?=?"fullname",?source?=?"name")
????DemoDto?toDemoDto(Demo?demo);
}
場景2:統一映射不同類型
下面例子中,time1、time2、time3都會被轉換,具體說明看下面的注釋:
/**
?*?來源對象
?*/
@Data
public?class?Demo?{
????private?Integer?id;
????private?String?name;
????/**
?????*?time1、time2名稱相同,time3轉為time33
?????*?這里的time1、time2、time33都是Date類型
?????*/
????private?Date?time1;
????private?Date?time2;
????private?Date?time3;
}
/**
?*?目標對象
?*/
@Data
public?class?DemoDto?{
????private?String?id;
????private?String?name;
????/**
?????*?這里的time1、time2、time33都是String類型
?????*/
????private?String?time1;
????private?String?time2;
????private?String?time33;
}
/**
?*?轉換器
?*/
@Mapper
public?interface?DemoMapper?{
????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class);
????@Mapping(target?=?"time33",?source?=?"time3")
????DemoDto?toDemoDto(Demo?demo);
????
????//MapStruct會將所有匹配到的:
????//源類型為Date、目標類型為String的屬性,
????//按以下方法進行轉換
????static?String?date2String(Date?date)?{
????????SimpleDateFormat?simpleDateFormat?=?new?SimpleDateFormat("yyyy-MM-dd?HH:mm:ss");
????????String?strDate?=?simpleDateFormat.format(date);
????????return?strDate;
????}
}
場景3:固定值、忽略某個屬性、時間轉字符串格式
一個例子演示三種用法,具體說明看注釋,很容易理解:
關鍵字:ignore、constant、dateFormat
/**
?*?來源對象
?*/
@Data
public?class?Demo?{
????private?Integer?id;
????private?String?name;
????private?Date?time;
}
/**
?*?目標對象
?*/
@Data
public?class?DemoDto?{
????private?String?id;
????private?String?name;
????private?String?time;
}
/**
?*?轉換器
?*/
@Mapper
public?interface?DemoMapper?{
????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class);
????//id屬性不賦值
????@Mapping(target?=?"id",?ignore?=?true)
????//name屬性固定賦值為“hello”
????@Mapping(target?=?"name",?constant?=?"hello")
????//time屬性轉為yyyy-MM-dd?HH:mm:ss格式的字符串
????@Mapping(target?=?"time",?dateFormat?=?"yyyy-MM-dd?HH:mm:ss")
????DemoDto?toDemoDto(Demo?demo);
}
場景4:為某個屬性指定轉換方法
場景2中,我們是按照某個轉換方法,統一將一種類型轉換為另外一種類型;而下面這個例子,是為某個屬性指定方法:
關鍵字:@Named注解、qualifiedByName
/**
?*?來源對象
?*/
@Data
public?class?Demo?{
????private?Integer?id;
????private?String?name;
}
/**
?*?目標對象
?*/
@Data
public?class?DemoDto?{
????private?String?id;
????private?String?name;
}
/**
?*?轉換器
?*/
@Mapper
public?interface?DemoMapper?{
????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class);
????//為name屬性指定@Named為convertName的方法進行轉換
????@Mapping(target?=?"name",?qualifiedByName?=?"convertName")
????DemoDto?toDemoDto(Demo?demo);
????@Named("convertName")
????static?String?aaa(String?name)?{
????????return?"姓名為:"?+?name;
????}
}
場景5:多個參數合并為一個對象
如果參數為多個的話,@Mapping注解中的source就要指定是哪個參數了,用點分隔:
關鍵字:點(.)
/**
?*?來源對象
?*/
@Data
public?class?Demo?{
????private?Integer?id;
????private?String?name;
}
/**
?*?目標對象
?*/
@Data
public?class?DemoDto?{
????private?String?fullname;
????private?String?timestamp;
}
/**
?*?轉換器
?*/
@Mapper
public?interface?DemoMapper?{
????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class);
????//fullname屬性賦值demo對象的name屬性(注意這里.的用法)
????//timestamp屬性賦值為傳入的time參數
????@Mapping(target?=?"fullname",?source?=?"demo.name")
????@Mapping(target?=?"timestamp",?source?=?"time")
????DemoDto?toDemoDto(Demo?demo,?String?time);
}
場景6:已有目標對象,將源對象屬性覆蓋到目標對象
覆蓋目標對象屬性時,一般null值不覆蓋,所以需要在類上的@Mapper注解中添加屬性:nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE 代表null值不進行賦值。
關鍵字:@MappingTarget注解、nullValuePropertyMappingStrategy
/**
?*?來源對象
?*/
@Data
public?class?Demo?{
????private?Integer?id;
????private?String?name;
}
/**
?*?目標對象
?*/
@Data
public?class?DemoDto?{
????private?String?id;
????private?String?name;
}
/**
?*?轉換器
?*/
@Mapper(unmappedTargetPolicy?=?ReportingPolicy.IGNORE,
????????nullValuePropertyMappingStrategy?=?NullValuePropertyMappingStrategy.IGNORE)
public?interface?DemoMapper?{
????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class);
????//將已有的目標對象當作一個參數傳進來
????DemoDto?toDemoDto(Demo?demo,?@MappingTarget?DemoDto?dto);
}
場景7:源對象兩個屬性合并為一個屬性
這種情況可以使用@AfterMapping注解。
關鍵字:@AfterMapping注解、@MappingTarget注解
/**
?*?來源對象
?*/
@Data
public?class?Demo?{
????private?Integer?id;
????private?String?firstName;
????private?String?lastName;
}
/**
?*?目標對象
?*/
@Data
public?class?DemoDto?{
????private?String?id;
????private?String?name;
}
/**
?*?轉換器
?*/
@Mapper
public?interface?DemoMapper?{
????DemoMapper?INSTANCE?=?Mappers.getMapper(DemoMapper.class);
????DemoDto?toDemoDto(Demo?demo);
????//在轉換完成后執(zhí)行的方法,一般用到源對象兩個屬性合并為一個屬性的場景
????//需要將源對象、目標對象(@MappingTarget)都作為參數傳進來,
????@AfterMapping
????static?void?afterToDemoDto(Demo?demo,?@MappingTarget?DemoDto?demoDto)?{
????????String?name?=?demo.getFirstName()?+?demo.getLastName();
????????demoDto.setName(name);
????}
}
小結
本文介紹了對象轉換工具 MapStruct 庫,以安全、簡潔、優(yōu)雅的方式來優(yōu)化我們的轉換代碼。
從文中的示例場景中可以看出,MapStruct 提供了大量的功能和配置,使我們可以快捷的創(chuàng)建出各種或簡單或復雜的映射器。而這些,也只是 MapStruct 庫的冰山一角,還有很多強大的功能文中沒有提到,感興趣的朋友可以自行查看官方文檔。
到此這篇關于詳解Java對象轉換神器MapStruct庫的使用的文章就介紹到這了,更多相關Java MapStruct內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringMVC中解決@ResponseBody注解返回中文亂碼問題
這篇文章主要介紹了SpringMVC中解決@ResponseBody注解返回中文亂碼問題, 小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04

