点击上方蓝色“方志朋”,选择“设为星标”

回复“666”获取独家整理的学习资料!

背景

我们的故事要从一个风和日丽的下午开始说起!

这天,外包韩在位置上写代码~外包韩根据如下定义

  • PO(persistant object):持久化对象,可以看成是与数据库中的表相映射的 java 对象。最简单的 PO 就是对应数据库中某个表中的一条记录。

  • VO(view object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。

  • BO(business object):业务对象,主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象。

  • DTO、DO(省略......)

将Bean进行逐一分类!例如一个car_tb的表,于是他有了两个类,一个叫CarPo,里头属性和表字段完全一致。另一个叫CarVo,用于页面上的Car显示!但是外包韩在做CarPo到CarVo转换的时候,代码是这么写的,伪代码如下:

CarPo carPo = this.carDao.selectById(1L);
CarVo carVo = new CarVo();
carVo.setId(carPo.getId());
carVo.setName(carPo.getName());
//省略一堆
return carVo;

*画外音:*看到这一串代码是不是特别亲切,我接手过一堆外包留下的代码,就是这么写的,一坨屎山!一类几千行,一半都在set属性。

恰巧,阿雄打水路过!鸡贼的阿雄瞄了一眼外包韩的屏幕,看到外包韩的这一系列代码!上去进行一顿教育,觉得不够优雅!阿雄觉得,应该用BeanUtils.copyProperties来简化书写,像下面这样!

CarPo carPo = this.carDao.selectById(1L);
CarVo carVo = new CarVo();
BeanUtils.copyProperties(carPo, carVo);
return carVo;

可是,外包韩盯着这段代码,说道:"网上不是说反射效率慢,你这么写,没有性能问题么?" 阿雄说道:" 如果是用Apache的BeanUtil类,确实有很大的性能问题,像阿里巴巴的代码扫描插件,都禁止用该类,如下所示!"

"但是,如果采用的是像Spring的BeanUtils类,要在调用次数足够多的时候,你才能明显的感受到卡顿。"阿雄补充道。

"哇,阿雄真棒!"外包韩兴奋不已!

看着这办公室基情满满的氛围。一旁正在拖地的清洁工------扫地烟,他决定不再沉默。

只见扫地烟扔掉手中的拖把,得瑟的说道"我们不考虑性能。从拓展性角度看看!BeanUtils还是有很多问题的!"

  • 复制对象时字段类型不一致,导致赋值不上,你怎么解决?自己拓展?

  • 复制对象时字段名称不一致,例如CarPo里叫carName,CarVo里叫name,导致赋值不上,你怎么解决?自己拓展?

  • 如果是集合类的复制,例如List转换为List,你怎么处理?(省略一万字....)

"那应该怎么办呢?"听了扫地烟的描述,外包韩疑惑的问道!

"很简单,其实我们在转换bean的过程中,set这些逻辑是固定的,唯一变化的就是转换规则。因此,如果我们只需要书写转换规则,转换代码由系统根据规则自动生成,就方便很多了!还是用上面的例子,CarPo里叫carName,CarVo里叫name,属性名称不一致。我们就通过一个注解

@Mapping(source = "carName", target = "name"),

指定对应转换规则。系统识别到这个注解,就会生成代码

carVo.setName(carPo.getCarName())

如果能以这样的方式,set代码由系统自动生成,那么在bean转换逻辑方面,灵活性将大大加强,而且这种方式不存在性能问题!"扫地烟补充道!

"那这些set逻辑,由什么工具来生成呢?"外包韩和阿雄一起问道!

"工具的名字叫MapStruct!"

ok,上面的故事到了这里,就结束了!不需要问结局,结局只有一个,外包韩和阿雄幸福美满的...(省略10000字)... 那么我们开始具体来说一说MapStruct

MapStruct的教程

这里从用法、原理、优势三个角度来介绍一下这个插件,至于详细教程,还是看官方文档吧。

用法

引入pom文件如下

<dependency><groupId>org.mapstruct</groupId><!-- jdk8以下就使用mapstruct --><artifactId>mapstruct-jdk8</artifactId><version>1.2.0.Final</version>
</dependency>
<dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.2.0.Final</version>
</dependency>

在准备两个实体类,为了方便演示,用了lombok插件。准备两个实体类,一个是CarPo

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CarPo {private Integer id;private String brand;
}

还有一个是CarVo

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CarVo {private Integer id;private String brand;
}

再来一个转换接口

@Mapper
public interface CarCovertBasic {CarCovertBasic INSTANCE =Mappers.getMapper(CarCovertBasic.class);CarVo toConvertVo(CarPo source);
}

测试代码如下:

//实际中从数据库取
CarPo carPo = CarPo.builder().id(1).brand("BMW").build();
CarVo carVo = CarCovertBasic.INSTANCE.toConvertVo(carPo);
System.out.println(carVo);

输出如下

CarVo(id=1, brand=BMW)

可以看到,carPo的属性值复制给了carVo。当然,在这种情况下,功能和BeanUtils是差不多的,体现不出优势!嗯,我们放在后面说,我们先来说说原理!

原理

其实原理就是MapStruct插件会识别我们的接口,生成一个实现类,在实现类中,为我们实现了set逻辑!例如,上面的例子中,给CarCovertBasic接口,实现了一个实现类CarCovertBasicImpl,我们可以用反编译工具看到源码如下图所示

下面,我们来说说优势

优势

(1)两个类型属性不一致

此时CarPo的一个属性为carName,而CarVo对应的属性为name!

我们在接口上增加对应关系即可,如下所示

@Mapper
public interface CarCovertBasic {
CarCovertBasic INSTANCE = Mappers.getMapper(CarCovertBasic.class);@Mapping(source = "carName", target = "name")
CarVo toConvertVo(CarPo source);
}

测试代码如下

CarPo carPo = CarPo.builder().id(1).brand("BMW").carName("宝马").build();
CarVo carVo = CarCovertBasic.INSTANCE.toConvertVo(carPo);
System.out.println(carVo);

输出如下

CarVo(id=1, brand=BMW, name=宝马)

可以看到carVo已经能识别到carPo中的carName属性,并赋值成功。反编译的图如下

画外音:如果有多个映射关系可以用@Mappings注解,嵌套多个@Mapping注解实现,后文说明!

(2)集合类型转换

如果我们要从List转换为List怎么办呢?简单,接口里加一个方法就行

@Mapper
public interface CarCovertBasic {CarCovertBasic INSTANCE = Mappers.getMapper(CarCovertBasic.class);@Mapping(source = "carName", target = "name")CarVo toConvertVo(CarPo source);List<CarVo> toConvertVos(List<CarPo> source);
}

如代码所示,我们增加了一个toConvertVos方法即可,mapStruct生成代码的时候,会帮我们去循环调用toConvertVo方法,给大家看一下反编译的代码,就一目了然

(3)类型不一致

在CarPo加一个属性为Date类型的createTime,而在CarVo加一个属性为String类型的createTime,那么代码如下

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CarPo {private Integer id;private String brand;private String carName;private Date createTime;
}@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CarVo {private Integer id;private String brand;private String name;private String createTime;
}

接口就可以这么写

@Mapper
public interface CarCovertBasic {CarCovertBasic INSTANCE = Mappers.getMapper(CarCovertBasic.class);@Mappings({@Mapping(source = "carName", target = "name"),@Mapping(target = "createTime", expression = "java(com.guduyan.util.DateUtil.dateToStr(source.getCreateTime()))")})CarVo toConvertVo(CarPo source);List<CarVo> toConvertVos(List<CarPo> source);
}

这样在代码中,就能解决类型不一致的问题!在生成set方法的时候,自动调用DateUtil类进行转换,由于比较简单,我就不贴反编译的图了!

(4)多对一

在实际业务情况中,我们有时候会遇到将两个Bean映射为一个Bean的情况,假设我们此时还有一个类为AtrributePo,我们要将CarPo和AttributePo同时映射为CarBo,我们可以这么写

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AttributePo {private double price;private String color;
}@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CarBo {private Integer id;private String brand;private String carName;private Date createTime;private double price;private String color;
}

接口改变如下

@Mapper
public interface CarCovertBasic {CarCovertBasic INSTANCE = Mappers.getMapper(CarCovertBasic.class);@Mappings({@Mapping(source = "carName", target = "name"),@Mapping(target = "createTime", expression = "java(com.guduyan.util.DateUtil.dateToStr(source.getCreateTime()))")})CarVo toConvertVo(CarPo source);List<CarVo> toConvertVos(List<CarPo> source);CarBo toConvertBo(CarPo source1, AttributePo source2);
}

直接增加接口即可,插件在生成代码的时候,会帮我们自动组装,看看下面的反编译代码就一目了然。

(5)其他

关于MapStruct还有其他很多的高级功能,我就不一一介绍了。大家可以参考下面的文档,在用到的时候自行翻阅即可!文档地址:https://mapstruct.org/documentation/reference-guide/

热门内容:
  • 一个 SpringBoot 项目该包含哪些?

  • 卸载Notepad++!事实已证明,它更牛逼……

  • 聊聊订单系统的设计?

  • 讨论:Service层需要接口吗?

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
明天见(。・ω・。)ノ♡

如何优雅的转换 Bean 对象!相关推荐

  1. 如何优雅的转换 Bean 对象?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 背景 我们的故事要从一个风和日丽的下午开始说起! 这天,外 ...

  2. Json转换Bean太过复杂?试试阿里巴巴的FastJSON

    阿里巴巴的JSON和BEAN的快速转化库FastJSON FastJSON 阿里巴巴JSON库:fastJson,既能解析JSON为Bean对象,又能转换Bean对象为JSON字符串]() 为什么使用 ...

  3. Java JSON、XML文件/字符串与Bean对象互转解析

    前言      在做web或者其他项目中,JSON与XML格式的数据是大家经常会碰见的2种.在与各种平台做数据对接的时候,JSON与XML格式也是基本的数据传递格式,本文主要简单的介绍JSON/XML ...

  4. IoC-spring 的灵魂(带你轻松理解IOC思想及bean对象的生成过程)

    在理解任何技术之前,我都会问自己一个问题:它的产生是为了解决什么样的问题,以及如何解决这些问题?希望你能在本篇文章中找到答案-- (由于大家对Ioc应该是经常使用了,所以这里不会告诉你应该怎么样使用, ...

  5. mongodb中Gson和java##Bean对象转化类

    此类使用感觉比较繁琐, 每个字段加注解才可以使用, 不如mongoTemplate使用方便, 但如果使用mongo客户端的话, 还是比手动拼接快一点, 所以贴在这儿 package com.iwher ...

  6. Spring源码分析-如何获取Bean对象

    导语   在上篇博客中 介绍了关于BeanFactory和FactoryBean相关的操作,并且查看了在两个操作中他们具体的代码有那些,这篇博客主要就是顺着上篇博客思路继续来分析Bean对象的获取.下 ...

  7. Java POJO Bean 对象与 Web Form 表单的自动装配

    PS: 我一直在找寻为什么 struts2有自动将form字段和getter setter 自动 匹配的功能, 这篇文章解答了我的疑惑 深度剖析:Java POJO Bean 对象与 Web Form ...

  8. java 对象验证非空_判断Bean对象指定字段非空

    判断Bean对象指定字段非空. 方案: 在bean对象上增加注解,指定字段非空,返回异常信息有明确字段描述,省去不必要的if.else判断. 新建注解类 /** * 参数校验,判断字段非空.返回异常文 ...

  9. Spring源码阅读之bean对象的创建过程

    Spring源码阅读之bean对象的创建过程 ​ Spring是通过IOC容器来管理对象的,该容器不仅仅只是帮我们创建了对象那么简单,它负责了对象的整个生命周期-创建.装配.销毁.这种方式成为控制反转 ...

最新文章

  1. R语言使用timeROC包计算存在竞争风险情况下的生存资料多时间AUC值、使用cox模型、并添加协变量、可视化存在竞争风险情况下的生存资料多时间ROC曲线
  2. HDU 1425 sort(堆排序)
  3. BugKuCTF 杂项 猜
  4. linux文件编程(2)——系统文件描述符、动静态文件、块设备介绍
  5. 酷乐Emlog新春特别版coolappy2.2开源
  6. 递归转手动管理栈的非递归
  7. 前端-requests-flask对应关系 HTTPBasicAuth
  8. mysql中grade字段降序排列_Mysql order by 多个字段排序
  9. linux 新唐 STM32,新唐M0系列stm32单片机哪个便宜?
  10. Vue中导出json【基于file-saver】
  11. tnl 的 masterServer, client server 架构学习笔记
  12. Python NLP 自然语言处理
  13. 建立matlab桌面遇到Error Staring Desktop
  14. android部分代码片段(例:判断设备为手机,获取mac地址,软键盘,唤醒屏幕等)
  15. php机房图形资产管理系统,机房资产管理系统(CMDB)
  16. 易盾php,GitHub - yidun/antispam-php-demo: 易盾反垃圾php演示
  17. Java 操作SSH2实现远程执行linux命令
  18. python实现api接口
  19. MAC 强制退出程序
  20. 2021-2027全球与中国冰球护具市场现状及未来发展趋势

热门文章

  1. Android sudio Day01-1
  2. VirtualBox装ghost XP
  3. 使用vue2.0 vue-router vuex 模拟ios7操作
  4. 详解Asp.net MVC DropDownLists
  5. 两个asp.net发送邮件类
  6. Windows Live Messenger  正式版已经发布
  7. win10+Chrome浏览器截长图方法
  8. 【Python】随机函数
  9. 【Codeforces】501B Misha and Changing Handles(map)
  10. c语言将水仙花数放入一维数组a中,全国计算机等级考试C语言考试程序设计题(13)...