当前位置:网站首页>【详细】快速实现对象映射的几种方式
【详细】快速实现对象映射的几种方式
2022-07-06 01:19:00 【菜鸟是大神】
项目开发过程中,经常需要编写model之间的转换,最常见的有:
- 实体转DTO
- DTO转实体
- …
举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // 实体:User @Data @Builder @NoArgsConstructor @AllArgsConstructor public class User { private Integer id; private String email; private String username; private String password; private Integer gender; private Date birthday; } // DTO:UserRegisterReq @Data public class UserRegisterReq { private String username; private String password; private String confirmPassword; private String email; } |
其中:
- UserRegisterReq是用户注册时,Controller层的请求入参
- User是用户实体
在执行注册时,我们需要将UserRegisterReq转换成User对象,再存储到数据库。此时,我们往往会编写类似如下的代码:
1 2 3 4 5 6 7 8 9 | @PostMapping("/users/reg") public void reg(@RequestBody UserRegisterReq userRegisterReq) { // 省略password 与 confirmPassword等值判断 User user = new User(); user.setEmail(userRegisterReq.getEmail()); user.setPassword(userRegisterReq.getPassword()); user.setUsername(userRegisterReq.getUsername()); // 保存user... } |
如上的代码虽然可行,但是如果类里面的field非常多,那么就会比较麻烦——我们写了一堆代码,只不过是为了实现对象的转换而已。
方法一、IDEA插件快速转换
IDEA提供GenerateAllSetter插件,可帮助我们快速生成上述代码。
- 插件主页:GenerateAllSetter - IntelliJ IDEs Plugin | Marketplace
- GitHub:GitHub - gejun123456/intellij-generateAllSetMethod: Intellij plugin to generate call to setter method value for class
演示如下图:
只需安装插件,然后按Alt + Enter(macOS则是Option + Enter),即可自动生成对象转换代码。
方法二、借助对象映射框架实现对象转换
方法一虽然很方便,但如果对象的字段非常多,那么还是会导致代码非常啰嗦,不够简洁。
事实上,Java生态有很多对象映射框架,专门帮助我们实现对象间的转换。这里笔者列出了业界相对常用的选项:
产品 | Dozer | Orika | MapStruct | CGLib BeanCopier | Spring BeanUtils | Apache BeanUtils |
---|---|---|---|---|---|---|
GitHub | dozer 1.9K stars | orika 1.2K stars | mapstruct 5K stars | cglib 4.3K stars | - | commons-beanutils 0.2K stars |
工作原理 | 大量反射,主要基于Field.set(obj, obj)为field赋值 | 基于javassist生成对象映射字节码,并加载生成的字节码文件 | 基于JSR269,在在编译期生成对象映射代码 | 基于ASM的MethodVisitor为field赋值 | 基于Spring反射工具类 | 基于反射 |
性能排名 | 5 | 2 | 1 | 4 | 3 | 6 |
虽然选项很多,但笔者目前只建议大家使用MapStruct。
MapStruct优势:
- 编译器生成Getter/Setter,无运行期性能损耗,性能强劲
- 基于JSR269,配置灵活
- 基于Getter/Setter,和自己手写Getter/Setter没有区别,搜索字段引用等较方便
缺点:
- 由于配置灵活,所以上手成本比其他组件稍微高一点点
MapStruct上手
配置IDE
参考 IDE Support – MapStruct ,配置你的IDE
整合
在项目中添加如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
<!-- ref: https://mapstruct.org/documentation/installation/ --> <properties> <org.mapstruct.version>1.4.2.Final</org.mapstruct.version> </properties> ... <dependencies> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version> </dependency> </dependencies> ... <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <!-- depending on your project --> <target>1.8</target> <!-- depending on your project --> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> <!-- other annotation processors --> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>
如果你的项目使用了Lombok,或使用了spring-boot-configuration-processor,则使用类似如下的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
<properties> <org.mapstruct.version>1.4.2.Final</org.mapstruct.version> </properties> ... <dependencies> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version> </dependency> </dependencies> ... <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>8</source> <target>8</target> <encoding>UTF-8</encoding> <!-- https://mapstruct.org/documentation/installation/ --> <!-- https://mapstruct.org/documentation/stable/reference/html/#lombok --> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </path> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok-mapstruct-binding</artifactId> <version>0.1.0</version> </path> <path> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <version>2.4.1</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build>
使用
定义接口,代码类似如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
import com.itmuch.gogolive1.domain.User; import com.itmuch.gogolive1.domain.UserRegisterReq; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; @Mapper public interface UserConverter { /** * 固定写法:Mappers.getMapper(接口名.class); */ UserConverter INSTANCE = Mappers.getMapper(UserConverter.class); User toUser(UserRegisterReq req); }
使用:
1 2 3 4 5 6
@PostMapping("/users/reg") public void reg2(@RequestBody UserRegisterReq userRegisterReq) { // 省略password 与 confirmPassword等值判断 User user = UserConverter.INSTANCE.toUser(userRegisterReq); // 保存user... }
由代码可知,只需如下代码,即可将UserRegisterReq转换User。
1
User user = UserConverter.INSTANCE.toUser(userRegisterReq);
原理
编译代码,并在前面的UserConverter接口上,按快捷键 Command + Option + B
(或点击 Navigate - Implementation(s)
) ,查找UserConverter的实现类,可跳转到类似如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @Generated( value = "org.mapstruct.ap.MappingProcessor", date = "2022-03-22T00:29:49+0800", comments = "version: 1.4.2.Final, compiler: javac, environment: Java 11.0.9.1 (Azul Systems, Inc.)" ) public class UserConverterImpl implements UserConverter { @Override public User toUser(UserRegisterReq req) { if ( req == null ) { return null; } UserBuilder user = User.builder(); user.email( req.getEmail() ); user.username( req.getUsername() ); user.password( req.getPassword() ); return user.build(); } } |
由代码可知,MapStruct在编译期间,生成了UserConverterImpl,并在其中实现了对象之间的转换。
和Spring整合
MapStruct支持与Spring整合,只需按如下步骤操作即可:
编写Mapper接口,并在其中添加
(componentModel = "spring")
属性:1 2 3 4
@Mapper(componentModel = "spring") public interface UserSpringConverter { User toUser(UserRegisterReq req); }
当使用时,只需注入
UserSpringConverter
即可:1 2
@Autowired UserSpringConverter userSpringConverter;
这是因为,使用
(componentModel = "spring")
后,生成的实现类会自动添加@Component
注解
拓展
本文只是介绍了较为简单的例子,事实上,MapStruct支持非常灵活的配置,例如:
枚举映射
1 2 3 4 5 6 7 8 9 10 11 12
@Mapper public interface OrderMapper { OrderMapper INSTANCE = Mappers.getMapper( OrderMapper.class ); @ValueMappings({ @ValueMapping(target = "SPECIAL", source = "EXTRA"), @ValueMapping(target = "DEFAULT", source = "STANDARD"), @ValueMapping(target = "DEFAULT", source = "NORMAL") }) ExternalOrderType orderTypeToExternalOrderType(OrderType orderType); }
自定义表达式映射:
1 2 3 4 5 6 7 8
@Mapper public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); @Mapping(target = "timeAndFormat", expression = "java( new org.sample.TimeAndFormat( s.getTime(), s.getFormat() ) )") Target sourceToTarget(Source s); }
边栏推荐
- The basic usage of JMeter BeanShell. The following syntax can only be used in BeanShell
- Basic process and testing idea of interface automation
- Spir - V premier aperçu
- 激动人心,2022开放原子全球开源峰会报名火热开启
- Paging of a scratch (page turning processing)
- 一图看懂!为什么学校教了你Coding但还是不会的原因...
- Construction plan of Zhuhai food physical and chemical testing laboratory
- 毕设-基于SSM高校学生社团管理系统
- 【第30天】给定一个整数 n ,求它的因数之和
- SPIR-V初窥
猜你喜欢
View class diagram in idea
Daily practice - February 13, 2022
Questions about database: (5) query the barcode, location and reader number of each book in the inventory table
ORA-00030
Idea sets the default line break for global newly created files
SSH login is stuck and disconnected
Huawei Hrbrid interface and VLAN division based on IP
yii中console方法调用,yii console定时任务
How to see the K-line chart of gold price trend?
Yii console method call, Yii console scheduled task
随机推荐
How to extract MP3 audio from MP4 video files?
程序员搞开源,读什么书最合适?
ORA-00030
Hundreds of lines of code to implement a JSON parser
MATLB|实时机会约束决策及其在电力系统中的应用
282. Stone consolidation (interval DP)
Recursive method to realize the insertion operation in binary search tree
Test de vulnérabilité de téléchargement de fichiers basé sur dvwa
False breakthroughs in the trend of London Silver
Use of crawler manual 02 requests
Condition and AQS principle
Unity | two ways to realize facial drive
记一个 @nestjs/typeorm^8.1.4 版本不能获取.env选项问题
Unity VR solves the problem that the handle ray keeps flashing after touching the button of the UI
CocoaPods could not find compatible versions for pod 'Firebase/CoreOnly'
FFT 学习笔记(自认为详细)
Xunrui CMS plug-in automatically collects fake original free plug-ins
ADS-NPU芯片架构设计的五大挑战
Threedposetracker project resolution
MobileNet系列(5):使用pytorch搭建MobileNetV3并基于迁移学习训练