Java中MapStruct對(duì)象映射的實(shí)現(xiàn)
簡(jiǎn)介
MapStruct是一種實(shí)體類映射框架,能夠通過(guò)Java注解將一個(gè)實(shí)體類的屬性安全地賦值給另一個(gè)實(shí)體類。有了mapstruct,只需要定義一個(gè)映射器接口,聲明需要映射的方法,在編譯過(guò)程中,mapstruct會(huì)自動(dòng)生成該接口的實(shí)現(xiàn)類,實(shí)現(xiàn)將源對(duì)象映射到目標(biāo)對(duì)象的效果。
MapStruct是基于JSR 269實(shí)現(xiàn)的,JSR 269是JDK引進(jìn)的一種規(guī)范。有了它,能夠?qū)崿F(xiàn)在編譯期處理注解,并且讀取、修改和添加抽象語(yǔ)法樹(shù)中的內(nèi)容。JSR 269使用Annotation Processor在編譯期間處理注解,Annotation Processor相當(dāng)于編譯器的一種插件,因此又稱為插入式注解處理。官網(wǎng)通道 | Github
優(yōu)點(diǎn)
安全性高:由于映射是在編譯期間實(shí)現(xiàn)的,如果編譯器能夠通過(guò),運(yùn)行期就不會(huì)報(bào)錯(cuò)。
高性能:編譯時(shí)生成bean映射的實(shí)現(xiàn)類,通過(guò)使?普通?法(getter/setter)調(diào)??不是反射來(lái)快速執(zhí)?。
缺點(diǎn)
使用復(fù)雜度:對(duì)于更復(fù)雜的映射,需要開(kāi)發(fā)人員編寫自定義映射接口和函數(shù)。
同類對(duì)比
映射工具 | 實(shí)現(xiàn)機(jī)制 | 性能對(duì)比 | 備注 |
Dozer | 反射機(jī)制 | 中 | 使用遞歸將數(shù)據(jù)從一個(gè)對(duì)象復(fù)制到另一個(gè)對(duì)象 |
Orika | 反射機(jī)制 | 中 | 同Dozer,不過(guò)Orika 使用字節(jié)碼生成 |
ModelMapper | 反射機(jī)制 | 中 | 簡(jiǎn)單易用,它根據(jù)約定確定對(duì)象之間的映射方式 |
JMapper | 編譯生成 | 高 | 基于Javassist 的Java映射框架 |
MapStruct | 編譯生成 | 高 | 在編譯時(shí)生成bean映射,以確保高性能、徹底的錯(cuò)誤檢查 |
快速入門
Maven依賴
<!-- 定義版本 --> <properties> <org.mapstruct.version>1.6.0</org.mapstruct.version> <org.projectlombok.mapstruct.version>0.2.0</org.projectlombok.mapstruct.version> </properties> <!-- 依賴包 --> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version> </dependency> <!--MapStruct會(huì)用到對(duì)象中的get、set方法,但get、set方法又需要lombok來(lái)生成。因此需要控制這兩者工作順序--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok-mapstruct-binding</artifactId> <version>${org.projectlombok.mapstruct.version}</version> </dependency>
編譯插件
<!-- 方式一--> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</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>17</source> <target>17</target> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>
Model定義
@Data @Builder @AllArgsConstructor @NoArgsConstructor public class UserDTO implements Serializable { private Integer id; private String userName; private String password; private Integer age; private String address; private String email; private List<UserRole> roles; } @Data @Builder @AllArgsConstructor @NoArgsConstructor public class UserVO implements Serializable { private Integer id; private String name; private String pwd; private Integer age; private String email; private List<UserRole> roles; } @Data @Builder @AllArgsConstructor @NoArgsConstructor public class UserRole implements Serializable { private Integer roleId; private String roleName; private String remark; }
Mapper定義
//@Mapper(componentModel = "spring") @Mapper public interface MapStructMapper { MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class); /** * 單個(gè)對(duì)象轉(zhuǎn)換 * @param userDTO * @return */ UserVO userDtoToVO(UserDTO userDTO); /** * 集合對(duì)象轉(zhuǎn)換 * @param userDTO * @return */ List<UserVO> userDtoToVOList(List<UserDTO> userDTO); }
重要:SpringBoot項(xiàng)目可以使用@Mapper(componentModel = "spring")的方式將bean交給spring容器進(jìn)行管理,因此可以不用寫MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class);
轉(zhuǎn)換調(diào)用
public void mapStructConvertTest(){ // 初始化用戶 UserDTO userDTO = this.instanceUser(); UserVO userVO = MapStructMapper.INSTANCE.userDtoToVO(userDTO); log.info("userVO:{}", JSON.toJSONString(userVO)); }
場(chǎng)景示例
常規(guī)映射
@Mapper public interface MapStructMapper { MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class); UserVO userDtoToVO(UserDTO userDTO); }
集合映射
@Mapper public interface MapStructMapper { MapStructMapper INSTANCE = Mappers.getMapper(MapStructMapper.class); List<UserVO> userDtoToVOList(List<UserDTO> userDTO); }
重要:集合轉(zhuǎn)換時(shí),必須先有單個(gè)對(duì)象的轉(zhuǎn)換函數(shù)。并且Mapper中只能有一個(gè)同類的原對(duì)象與目標(biāo)對(duì)象的轉(zhuǎn)換,否則集合轉(zhuǎn)換不知道取哪一個(gè)單對(duì)象轉(zhuǎn)換函數(shù)。
單字段映射
@Mapping(target = "pwd", source = "password") UserVO userDtoToVO(UserDTO userDTO);
多字段映射
@Mappings({ @Mapping(target = "pwd", source = "password"), @Mapping(target = "name", source = "userName"), }) UserVO userDtoToVOMoreField(UserDTO userDTO);
忽略字段
@Mapping(target = "email", ignore = true) UserVO userDtoToVOIgnoreField(UserDTO userDTO);
常量值映射
@Mapping(target = "constant", constant = "OK") UserVO newUserWithConstant(UserDTO userDTO);
默認(rèn)值映射
@Mapping(source = "email", target = "email", defaultValue = "默認(rèn)值") UserVO userDtoToVONullDefaultValue(UserDTO userDTO);
表達(dá)式映射
@Mapping(target = "fullName", expression = "java(userDTO.getUserName() + ' ' + userDTO.getAddress())") UserVO userDtoToVOExpression(UserDTO userDTO);
@Mapping(target = "email", expression = "java(!userDTO.getEmail().isEmpty()? \"不為空\(chéng)" : \"為空\(chéng)")") UserVO userDtoToVOWithCondition(UserDTO userDTO);
執(zhí)行函數(shù)
@Mapping(target = "email", source = "email", qualifiedByName = "toUpperCase") UserVO emailToUpperCase(UserDTO userDTO); @Named("toUpperCase") default String toUpperCase(String value) { // 轉(zhuǎn)換大寫 return value == null ? null : value.toUpperCase(); }
深拷貝
@Mapper(componentModel = "spring",mappingControl = DeepClone.class) public interface MapStructMapper { }
說(shuō)明:mappingControl = DeepClone.class 是指定深拷貝模式,不指定則默認(rèn)淺拷貝,淺拷貝時(shí)集合類是底層是調(diào)用Array 的copy 方法。如果是深拷貝模式,MapStruct框架會(huì)生成集合遍歷代碼,集合中元素如果是引用類型會(huì)生成引用類型轉(zhuǎn)換代碼,層層轉(zhuǎn)換,深度拷貝。集合類拷貝的限制比較多,不支持多層嵌套集合類深拷貝,而且要求源字段和目標(biāo)字段集合類型嚴(yán)格一致。
淺拷貝 :只復(fù)制對(duì)象的引用,而不會(huì)復(fù)制對(duì)象本身的內(nèi)容。如果更改了原始對(duì)象的一個(gè)地址,DTO中的地址也會(huì)跟著改變,因?yàn)樗鼈冎赶虻氖峭粋€(gè)對(duì)象。
深拷貝:會(huì)遞歸地復(fù)制對(duì)象的所有內(nèi)容,包括嵌套的對(duì)象。即使你更改了原始對(duì)象中的數(shù)據(jù),DTO中的數(shù)據(jù)也不會(huì)受到影響。
逆向映射
/** * 單個(gè)對(duì)象映射 * @param userDTO * @return */ UserVO userDtoToVO(UserDTO userDTO); /** * 逆向映射 * @param userVO 源 VO 對(duì)象 * @return 目標(biāo) DTO 對(duì)象 */ @InheritInverseConfiguration UserDTO userVOToDto(UserVO userVO);
說(shuō)明:正向函數(shù)有業(yè)務(wù)邏輯處理或?qū)傩灶愋筒黄ヅ涞牟荒苣嫦蛴成?/p>
映射后執(zhí)行動(dòng)作
@AfterMapping default void afterMapping(UserDTO userDTO, @MappingTarget UserVO userVO) { // Add custom post-mapping logic here userVO.setId(10000); System.out.println("afterMapping:id設(shè)置為:"+userVO.getId()); }
延伸內(nèi)容
MapStruct Plus 是 MapStruct 的增強(qiáng)工具,在 MapStruct 的基礎(chǔ)上,實(shí)現(xiàn)了自動(dòng)生成 Mapper 接口的功能,并強(qiáng)化了部分功能,使 Java 類型轉(zhuǎn)換更加便捷、優(yōu)雅。官網(wǎng)通道 | GitHub
Maven依賴
<properties> <mapstruct-plus.version>1.4.5</mapstruct-plus.version> </properties> <dependency> <groupId>io.github.linpeilie</groupId> <artifactId>mapstruct-plus-spring-boot-starter</artifactId> <version>${mapstruct-plus.version}</version> </dependency>
新增配置類
@ComponentModelConfig(componentModel = "default") public class MapperConfiguration { }
對(duì)象映射
@AutoMapper(target = UserDto.class) @Data public class User { // ... }
轉(zhuǎn)換Map
@AutoMapMapper @Data public class MapModelB { private Date date; }
一個(gè)類轉(zhuǎn)換為多個(gè)類
@Data @AutoMappers({ @AutoMapper(target = UserDto.class), @AutoMapper(target = UserVO.class) }) public class User { // fields }
循環(huán)嵌套
@Data @AutoMapper(target = TreeNodeDto.class, cycleAvoiding = true) public class TreeNode { private TreeNode parent; private List<TreeNode> children; } @Data @AutoMapper(target = TreeNode.class, cycleAvoiding = true) public class TreeNodeDto { private TreeNodeDto parent; private List<TreeNodeDto> children; }
到此這篇關(guān)于Java中MapStruct對(duì)象映射的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)MapStruct對(duì)象映射內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA?2022最新激活碼注冊(cè)碼超詳細(xì)教程(親測(cè)激活有效)
這篇文章主要介紹了IDEA?2022最新激活碼超詳細(xì)教程(親測(cè)激活至2099年),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Elasticsearch?percolate?查詢示例詳解
這篇文章主要為大家介紹了Elasticsearch?percolate?查詢示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01MyBatis注解開(kāi)發(fā)-@Insert和@InsertProvider的使用
這篇文章主要介紹了MyBatis注解開(kāi)發(fā)-@Insert和@InsertProvider的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2022-07-07SpringBoot統(tǒng)一數(shù)據(jù)返回的幾種方式
在Web應(yīng)用程序開(kāi)發(fā)中,統(tǒng)一數(shù)據(jù)返回格式對(duì)于前后端分離項(xiàng)目尤為重要,本文就來(lái)介紹一下SpringBoot統(tǒng)一數(shù)據(jù)返回的幾種方式,具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07關(guān)于重寫equals()方法和hashCode()方法及其簡(jiǎn)單的應(yīng)用
這篇文章主要介紹了關(guān)于重寫equals()方法和hashCode()方法及其簡(jiǎn)單的應(yīng)用,網(wǎng)上的知識(shí)有些可能是錯(cuò)誤的,關(guān)于?equals()?方法的理解,大家討論不一樣,需要的朋友可以參考下2023-04-04