Java中基于注解的代碼生成工具M(jìn)apStruct映射使用詳解
介紹
在 Java 開(kāi)發(fā)中,我們經(jīng)常需要在不同的 Java Bean 之間進(jìn)行數(shù)據(jù)映射,比如從實(shí)體類(lèi)(Entity)到數(shù)據(jù)傳輸對(duì)象(DTO)的轉(zhuǎn)換。傳統(tǒng)的做法是手動(dòng)編寫(xiě)大量的 setter 和 getter 方法來(lái)完成屬性的賦值,這種方式不僅繁瑣,而且容易出錯(cuò)。MapStruct 作為一個(gè)基于注解的代碼生成工具,為我們提供了一種更加優(yōu)雅、高效的解決方案。它在編譯時(shí)自動(dòng)生成映射代碼,避免了運(yùn)行時(shí)反射帶來(lái)的性能開(kāi)銷(xiāo),同時(shí)保證了類(lèi)型安全。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 高性能:MapStruct 在編譯階段生成映射代碼,運(yùn)行時(shí)直接調(diào)用這些代碼,避免了反射的使用,從而顯著提高了性能。
- 類(lèi)型安全:由于映射代碼是在編譯時(shí)生成的,編譯器會(huì)對(duì)類(lèi)型進(jìn)行檢查,因此可以在編譯階段發(fā)現(xiàn)類(lèi)型不匹配等錯(cuò)誤,避免了運(yùn)行時(shí)異常。
- 代碼簡(jiǎn)潔:使用 MapStruct 只需要定義映射接口和使用注解進(jìn)行配置,無(wú)需手動(dòng)編寫(xiě)大量的映射邏輯,大大減少了代碼量,提高了開(kāi)發(fā)效率。
缺點(diǎn)
- 學(xué)習(xí)成本:需要學(xué)習(xí) MapStruct 提供的各種注解及其使用方法,對(duì)于初學(xué)者來(lái)說(shuō)可能有一定的學(xué)習(xí)曲線。
- 依賴管理:需要在項(xiàng)目中引入 MapStruct 的相關(guān)依賴,增加了項(xiàng)目的依賴管理復(fù)雜度。
核心注解及詳細(xì)使用語(yǔ)法說(shuō)明
@Mapper
- 作用:用于標(biāo)記一個(gè)接口為映射接口,MapStruct 會(huì)在編譯時(shí)為該接口生成具體的實(shí)現(xiàn)類(lèi)。
- 使用語(yǔ)法:
import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; @Mapper public interface UserMapper { // 通過(guò) Mappers.getMapper 方法獲取映射接口的實(shí)例 UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); // 定義從 UserEntity 到 UserDTO 的映射方法 UserDTO toDTO(UserEntity entity); // 定義從 UserDTO 到 UserEntity 的映射方法 UserEntity toEntity(UserDTO dto); }
@Mapping
- 作用:用于指定源對(duì)象和目標(biāo)對(duì)象之間的屬性映射關(guān)系,當(dāng)源對(duì)象和目標(biāo)對(duì)象的屬性名
不一致
時(shí),可以使用該注解進(jìn)行顯式映射
。 - 使用語(yǔ)法:
import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @Mapper public interface UserMapper { UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); // 使用 @Mapping 注解指定 entityId 映射到 id,entityName 映射到 name @Mapping(source = "entityId", target = "id") @Mapping(source = "entityName", target = "name") UserDTO toDTO(UserEntity entity); // 反向映射 @Mapping(source = "id", target = "entityId") @Mapping(source = "name", target = "entityName") UserEntity toEntity(UserDTO dto); }
@Mappings
- 作用:@Mappings 是 @Mapping 的集合形式,用于一次性指定
多個(gè)屬性映射
關(guān)系。 - 使用語(yǔ)法:
import org.mapstruct.Mapper; import org.mapstruct.Mappings; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @Mapper public interface UserMapper { UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); @Mappings({ @Mapping(source = "entityId", target = "id"), @Mapping(source = "entityName", target = "name") }) UserDTO toDTO(UserEntity entity); @Mappings({ @Mapping(source = "id", target = "entityId"), @Mapping(source = "name", target = "entityName") }) UserEntity toEntity(UserDTO dto); }
@Context
- 作用:用于在映射過(guò)程中傳遞上下文信息,比如一些輔助對(duì)象,這些對(duì)象可以在映射方法中使用。
- 使用語(yǔ)法:
import org.mapstruct.Mapper; import org.mapstruct.Context; import org.mapstruct.factory.Mappers; import java.util.Locale; @Mapper public interface UserMapper { UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); // 使用 @Context 注解傳遞 Locale 對(duì)象作為上下文信息 UserDTO toDTO(UserEntity entity, @Context Locale locale); }
@AfterMapping
- 作用:用于在映射完成后執(zhí)行自定義的邏輯,比如對(duì)目標(biāo)對(duì)象的某些屬性進(jìn)行額外的處理。
- 使用語(yǔ)法:
import org.mapstruct.Mapper; import org.mapstruct.AfterMapping; import org.mapstruct.MappingTarget; import org.mapstruct.factory.Mappers; @Mapper public interface UserMapper { UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); UserDTO toDTO(UserEntity entity); // 使用 @AfterMapping 注解定義映射完成后的自定義邏輯 @AfterMapping default void afterMapping(@MappingTarget UserDTO dto, UserEntity entity) { // 將源對(duì)象的 firstName 和 lastName 拼接后賦值給目標(biāo)對(duì)象的 fullName 屬性 dto.setFullName(entity.getFirstName() + " " + entity.getLastName()); } }
Demo示例
公共基本類(lèi)定義
import lombok.Data; // 用戶實(shí)體類(lèi) @Data public class UserEntity { private Long id; private String name; private Integer age; private String firstName; private String lastName; private AddressEntity address; private Long entityId; private String entityName; } // 用戶數(shù)據(jù)傳輸對(duì)象類(lèi) @Data public class UserDTO { private Long id; private String name; private Integer age; private String fullName; private AddressDTO address; private Long entityId; private String entityName; } // 地址實(shí)體類(lèi) @Data public class AddressEntity { private String street; private String city; } // 地址數(shù)據(jù)傳輸對(duì)象類(lèi) @Data public class AddressDTO { private String street; private String city; }
簡(jiǎn)單映射示例
import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; @Mapper public interface UserMapper { // 獲取映射接口的實(shí)例 UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); // 從 UserEntity 到 UserDTO 的映射方法 UserDTO toDTO(UserEntity entity); // 從 UserDTO 到 UserEntity 的映射方法 UserEntity toEntity(UserDTO dto); } // 測(cè)試代碼 public class MainSimpleMapping { public static void main(String[] args) { UserEntity userEntity = new UserEntity(); userEntity.setId(1L); userEntity.setName("John"); userEntity.setAge(25); // 使用映射接口的實(shí)例進(jìn)行映射 UserDTO userDTO = UserMapper.INSTANCE.toDTO(userEntity); System.out.println("簡(jiǎn)單映射示例結(jié)果:"); System.out.println("UserDTO: id=" + userDTO.getId() + ", name=" + userDTO.getName() + ", age=" + userDTO.getAge()); } }
輸出結(jié)果:
簡(jiǎn)單映射示例結(jié)果:
UserDTO: id=1, name=John, age=25
字段名不一致的映射示例
import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @Mapper public interface UserMapper { UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); @Mapping(source = "entityId", target = "id") @Mapping(source = "entityName", target = "name") UserDTO toDTO(UserEntity entity); @Mapping(source = "id", target = "entityId") @Mapping(source = "name", target = "entityName") UserEntity toEntity(UserDTO dto); } // 測(cè)試代碼 public class MainFieldNameMismatch { public static void main(String[] args) { UserEntity userEntity = new UserEntity(); userEntity.setEntityId(1L); userEntity.setEntityName("John"); userEntity.setAge(25); UserDTO userDTO = UserMapper.INSTANCE.toDTO(userEntity); System.out.println("字段名不一致映射示例結(jié)果:"); System.out.println("UserDTO: id=" + userDTO.getId() + ", name=" + userDTO.getName() + ", age=" + userDTO.getAge()); } }
輸出結(jié)果:
字段名不一致映射示例結(jié)果:
UserDTO: id=1, name=John, age=25
嵌套對(duì)象映射示例
import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; @Mapper public interface UserMapper { UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); UserDTO toDTO(UserEntity entity); UserEntity toEntity(UserDTO dto); AddressDTO toDTO(AddressEntity entity); AddressEntity toEntity(AddressDTO dto); } // 測(cè)試代碼 public class MainNestedObjectMapping { public static void main(String[] args) { AddressEntity addressEntity = new AddressEntity(); addressEntity.setStreet("123 Main St"); addressEntity.setCity("New York"); UserEntity userEntity = new UserEntity(); userEntity.setId(1L); userEntity.setName("John"); userEntity.setAddress(addressEntity); UserDTO userDTO = UserMapper.INSTANCE.toDTO(userEntity); System.out.println("嵌套對(duì)象映射示例結(jié)果:"); System.out.println("UserDTO: id=" + userDTO.getId() + ", name=" + userDTO.getName()); System.out.println("AddressDTO: street=" + userDTO.getAddress().getStreet() + ", city=" + userDTO.getAddress().getCity()); } }
輸出結(jié)果:
嵌套對(duì)象映射示例結(jié)果:
UserDTO: id=1, name=John
AddressDTO: street=123 Main St, city=New York
自定義映射邏輯示例
import org.mapstruct.Mapper; import org.mapstruct.AfterMapping; import org.mapstruct.MappingTarget; import org.mapstruct.factory.Mappers; @Mapper public interface UserMapper { UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); UserDTO toDTO(UserEntity entity); @AfterMapping default void afterMapping(@MappingTarget UserDTO dto, UserEntity entity) { dto.setFullName(entity.getFirstName() + " " + entity.getLastName()); } } // 測(cè)試代碼 public class MainCustomMappingLogic { public static void main(String[] args) { UserEntity userEntity = new UserEntity(); userEntity.setId(1L); userEntity.setFirstName("John"); userEntity.setLastName("Doe"); UserDTO userDTO = UserMapper.INSTANCE.toDTO(userEntity); System.out.println("自定義映射邏輯示例結(jié)果:"); System.out.println("UserDTO: id=" + userDTO.getId() + ", fullName=" + userDTO.getFullName()); } }
輸出結(jié)果:
自定義映射邏輯示例結(jié)果:
UserDTO: id=1, fullName=John Doe
使用上下文示例
import org.mapstruct.Mapper; import org.mapstruct.Context; import org.mapstruct.factory.Mappers; import java.util.Locale; @Mapper public interface UserMapper { UserMapper INSTANCE = Mappers.getMapper(UserMapper.class); UserDTO toDTO(UserEntity entity, @Context Locale locale); default String localize(String value, @Context Locale locale) { // 根據(jù) locale 進(jìn)行本地化處理 return value; } } // 測(cè)試代碼 public class MainWithContext { public static void main(String[] args) { UserEntity userEntity = new UserEntity(); userEntity.setId(1L); userEntity.setName("John"); Locale locale = Locale.US; UserDTO userDTO = UserMapper.INSTANCE.toDTO(userEntity, locale); System.out.println("使用上下文示例結(jié)果:"); System.out.println("UserDTO: id=" + userDTO.getId() + ", name=" + userDTO.getName()); } }
輸出結(jié)果:
使用上下文示例結(jié)果:
UserDTO: id=1, name=John
通過(guò)以上示例可以看到,使用 MapStruct 能夠方便快捷地完成 Java Bean 之間的映射,同時(shí)結(jié)合 Lombok 的 @Data 注解進(jìn)一步簡(jiǎn)化了代碼。并且從輸出結(jié)果可以直觀地驗(yàn)證各個(gè)映射場(chǎng)景的正確性。
到此這篇關(guān)于Java中基于注解的代碼生成工具M(jìn)apStruct映射使用詳解的文章就介紹到這了,更多相關(guān)Java MapStruct內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Spring通過(guò)@Value注解注入屬性的幾種方式
本篇文章主要介紹了詳解Spring通過(guò)@Value注解注入屬性的幾種方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03SpringBootTest單元測(cè)試報(bào)錯(cuò)的解決方案
這篇文章主要介紹了SpringBootTest單元測(cè)試報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09輸出java進(jìn)程的jstack信息示例分享 通過(guò)線程堆棧信息分析java線程
通過(guò)ps到j(luò)ava進(jìn)程號(hào)將進(jìn)程的jstack信息輸出。jstack信息是java進(jìn)程的線程堆棧信息,通過(guò)該信息可以分析java的線程阻塞等問(wèn)題。2014-01-01java通過(guò)模擬post方式提交表單實(shí)現(xiàn)圖片上傳功能實(shí)例
這篇文章主要介紹了java通過(guò)模擬post方式提交表單實(shí)現(xiàn)圖片上傳功能實(shí)例,涉及Java針對(duì)表單的提交操作響應(yīng)及文件傳輸?shù)南嚓P(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11JAVA WEB中Servlet和Servlet容器的區(qū)別
這篇文章主要介紹了JAVA WEB中Servlet和Servlet容器的區(qū)別,文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06SpringBoot中整合Minio文件存儲(chǔ)的安裝部署過(guò)程
這篇文章主要介紹了SpringBoot整合Minio文件存儲(chǔ)的相關(guān)知識(shí),詳細(xì)介紹了Minio安裝部署過(guò)程,需要的朋友可以參考下2022-04-04