背景/場景
我們在編程過程中,經常存在實體類的轉換。如數據庫層對象pojo類 與 底層對外傳輸的對象 xxResponse類。一般常用的方式是每個值去get/set。幸運的是,我們有很多框架可以解決這種問題。
常見的工具類
- Spring BeanUtils
- Apache BeanUtils
- Dozer
- Orika
- MapStruct
- ModelMapper
- JMapper
那我們如何選擇這些工具呢
性能對比
不囉嗦,直接上圖
總之,就是 MapStruct性能在各種綜合情況下最優
MapStruct使用
maven依賴引入
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.3.1.Final</version>
</dependency>
<!-- lombok插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
打包
<build>
<plugins>
<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.3.1.Final</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
示例代碼(拷貝到IDE可直接運行)
public class Test {
public static void main(String[] args) {
UserDo userDo = new UserDo("zhangsan", 20, "酒仙橋");
UserResp userResp = SourceMapper.MAPPER.convert(userDo);
System.out.println( JSON.toJSONString(userResp));
}
}
/**
* 原始對象
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
class UserDo {
private String name;
private int age;
private String address;
}
/**
* 返回給前端的對象
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
class UserResp {
private String name;
//這裏故意少掉 age屬性,看缺失字段是否會成功
//這裏故意寫成 addr屬性,測試名稱不同能否轉換
private String addr;
}
/**
* 轉換器
*/
@Mapper
interface SourceMapper{
SourceMapper MAPPER = Mappers.getMapper( SourceMapper.class );
/**
* 對象轉換時參數不對應使用 @Mapping註解 顯示指定值的對應關係
* 如果存在多個可使用 @Mappings註解,裏面是個集合
* 如 @Mappings(value = {
* @Mapping(source = "aa", target = "bb" ),
* @Mapping(source = "cc", target = "dd" )})
* @param userDo
* @return
*/
@Mapping( source = "address", target = "addr" )
UserResp convert( UserDo userDo );
}
代碼編譯後
在 target/generated-sources/annotations 目錄會自動生成 SourceMapper的實現類
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2020-05-23T13:08:56+0800",
comments = "version: 1.3.1.Final, compiler: javac, environment: Java 1.8.0_60 (Oracle Corporation)"
)
class SourceMapperImpl implements SourceMapper {
@Override
public UserResp convert(UserDo userDo) {
if ( userDo == null ) {
return null;
}
UserResp userResp = new UserResp();
userResp.setAddr( userDo.getAddress() );
userResp.setName( userDo.getName() );
return userResp;
}
}
安裝 MapStruct 插件
在 IDEA 中依次打開 File - > Settings - > Plugins
然後在 Markeyplace 搜索框中輸入 mapstruct,點擊 install,然後重啓 IDE 即可,不再囉嗦啦
MapStruct技術總結
技術要領
- 需要寫個接口做轉換器
- 接口類上需要聲明 @Mapper 註解
- 接口中需聲明 SourceMapper MAPPER = Mappers.getMapper( SourceMapper.class );
- 接口中寫要轉換的接口:
- 參數爲原始對象,
- 返回爲 要返回的對象
- 接口上聲明要轉換的具體參數
框架優點
- 相對反射來說
- 框架使用註解生成代碼,運行時比較高效。
- 反射debug困難,這裏debug直接到生成的代碼中了
- 編寫層面來說
- 寫法快捷高效
- 手寫 set 方法屬性太多還容易漏,這裏不會
- 名字不對應恰好又不想返回的屬性自動過濾,簡直不要太方便了
參考
- 芋道源碼mapStruct文章:http://www.iocoder.cn/Spring-Boot/MapStruct/
- 框架/工具性能對比:https://www.baeldung.com/java-performance-mapping-frameworks
- mapstruct官網:https://mapstruct.org/
- 官方文檔:https://mapstruct.org/documentation/stable/reference/html/#shared-configurations
- 官方git代碼示例:https://github.com/mapstruct/mapstruct-examples