在我們?nèi)粘i_發(fā)的程序中,為了各層之間解耦,一般會定義不同的對象用來在不同層之間傳遞數(shù)據(jù),比如xxxDTO、xxxVO、xxxQO,當在不同層之間傳輸數(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;
}
/**
* 目標對象
*/
@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ù)為來源對象,返回值為目標對象
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);
//輸出結(jié)果:目標對象demoDto為:DemoDto(id=111, name=hello)
}
測試結(jié)果如下:
目標對象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;
}
/**
* 目標對象
*/
@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;
}
/**
* 目標對象
*/
@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、目標類型為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;
}
/**
* 目標對象
*/
@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;
}
/**
* 目標對象
*/
@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;
}
/**
* 目標對象
*/
@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:已有目標對象,將源對象屬性覆蓋到目標對象
覆蓋目標對象屬性時,一般null值不覆蓋,所以需要在類上的@Mapper注解中添加屬性:nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE 代表null值不進行賦值。
關(guān)鍵字:@MappingTarget注解、nullValuePropertyMappingStrategy
/**
* 來源對象
*/
@Data
public class Demo {
private Integer id;
private String name;
}
/**
* 目標對象
*/
@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);
//將已有的目標對象當作一個參數(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;
}
/**
* 目標對象
*/
@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í)行的方法,一般用到源對象兩個屬性合并為一個屬性的場景
//需要將源對象、目標對象(@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)注
關(guān)注
1文章
38瀏覽量
17368 -
傳輸數(shù)據(jù)
+關(guān)注
關(guān)注
1文章
110瀏覽量
16081 -
代碼生成器
+關(guān)注
關(guān)注
0文章
25瀏覽量
9080
發(fā)布評論請先 登錄
相關(guān)推薦
評論