当前位置:首页 > 科技  > 软件

为什么不推荐使用 BeanUtils.copyProperties?

来源: 责编: 时间:2024-06-05 17:45:21 78观看
导读在日常开发中,经常涉及到 VO、DTO、DO等对象之间的属性拷贝,为了避免使用原始的setter和getter方法,我们通常过借助一些三方工具,本文我们将聊聊某程序员使用BeanUtils.copyProperties工具,导致差点被开除的血泪史。一、Be

在日常开发中,经常涉及到 VO、DTO、DO等对象之间的属性拷贝,为了避免使用原始的setter和getter方法,我们通常过借助一些三方工具,本文我们将聊聊某程序员使用BeanUtils.copyProperties工具,导致差点被开除的血泪史。KXV28资讯网——每日最新资讯28at.com

KXV28资讯网——每日最新资讯28at.com

一、BeanUtils.copyProperties是什么?

BeanUtils.copyProperties是一个对象拷贝的常用工具,Spring和Apache都提供了对应的静态方法,两者源码如下:KXV28资讯网——每日最新资讯28at.com

// org.springframework.beans.BeanUtilspublic static void copyProperties(Object source, Object target) throws BeansException {    copyProperties(source, target, null, (String[]) null);}// org.apache.commons.beanutils.BeanUtilspublic static void copyProperties(final Object dest, final Object orig)        throws IllegalAccessException, InvocationTargetException {    BeanUtilsBean.getInstance().copyProperties(dest, orig);}

通过上述两个源码方法可以发现:两个方法中的入参源对象和目标对象 顺序是反的,所以在使用时,一定要注意具体导入的是哪一个BeanUtils,切勿把入参顺序搞反。KXV28资讯网——每日最新资讯28at.com

接着,分别解析两种方式的源码实现原理:KXV28资讯网——每日最新资讯28at.com

1.Spring实现

org.springframework.beans.BeanUtils的源码实现如下图:KXV28资讯网——每日最新资讯28at.com

KXV28资讯网——每日最新资讯28at.com

整个源码的实现逻辑总结成下面 7个步骤:KXV28资讯网——每日最新资讯28at.com

  1. Spring的 BeanUtils拷贝,使用的是反射机制
  2. 先获取target中所有字段以及它们的getter和setter方法
  3. 遍历target的字段,如果字段有setter方法或者不是忽略对象则进行下一步操作,否则忽略
  4. 用target的字段去source中获取对应的值(通过getter方法),有值则进行下一步,否则忽略
  5. 获source和target中同一个字段的类型,并且判断类型是否相同,相同则继续下一步,否则忽略
  6. 如果source和target的字段是非public,则通过反射修改权限
  7. 最后,通过反射完成赋值
  8. 通过源码分析,我们能够看出org.springframework.beans.BeanUtils的拷贝屏蔽了很多的异常,总结如下:
  • source和target的字段缺少getter和setter方法,拷贝失败
  • source和target的字段名称不同,拷贝失败,即字段名相同才可以拷贝
  • source和target的字段类型不同,拷贝失败,即类型相同才可以拷贝
  • 对于Map类型,无法拷贝

对于上述前 3种拷贝失败的场景,编译期间无法感知,一旦代码上线大概率会出 bug,另外,因为使用的反射机制,性能略有影响。KXV28资讯网——每日最新资讯28at.com

2.Apache实现

org.apache.commons.beanutils.BeanUtils的源码实现如下图:KXV28资讯网——每日最新资讯28at.com

KXV28资讯网——每日最新资讯28at.com

通过源码我们能够看出:Apache的实现其实是对Spring的一种增强,增加了DynaBean和Map两种类型的拷贝,它们的实现都是采用反射机制。KXV28资讯网——每日最新资讯28at.com

另外,Spring和 Apache的两种实现方案都是浅拷贝,也就是说,如果对象中还有内嵌对象,如果不做额外处理,拷贝会失败。KXV28资讯网——每日最新资讯28at.com

所谓浅拷贝,浅拷贝是一种复制对象的方式,它创建一个新对象,这个新对象是原对象的副本,但对于对象中引用类型的字段,浅拷贝只复制它们的引用,而不复制它们所指向的实际对象。换句话说,浅拷贝只拷贝对象的第一层属性,对于属性中的引用类型,只拷贝引用地址。KXV28资讯网——每日最新资讯28at.com

如下示例,当source内部Inner对象的 address字段更改了,target的也跟着变更了:KXV28资讯网——每日最新资讯28at.com

KXV28资讯网——每日最新资讯28at.com

二、为什么不推荐BeanUtils.copyProperties

在上面源码分析的过程中可以发现:只有同时满足下面 3个条件才能拷贝成功:KXV28资讯网——每日最新资讯28at.com

  • source和target的字段需要有getter和setter方法
  • source和target的字段名称需要相同
  • source和target的字段类型需要相同

以上 3个条件缺失任何一个拷贝都会失败,但是编译器无法感知,对程序员不友好。KXV28资讯网——每日最新资讯28at.com

假如,在开发中忘记写getter和setter,使用BeanUtils.copyProperties拷贝不会有异常,但是业务逻辑上没有达到预期,所以这种异常要么在测试中发现,要么需要跑真实的业务逻辑才能发现。KXV28资讯网——每日最新资讯28at.com

还有一种场景,假如source中有个money字段一开始被程序员A定义成double类型,后面被程序员B 修改成了BigDecimal,程序员B发现代码没有报错,而且是一个小修改就直接上线了。KXV28资讯网——每日最新资讯28at.com

1天后,有人反馈线上出问题了,经过好一番努力地排查发现,使用BeanUtils.copyProperties拷贝,source中的money字段是BigDecimal类型,而target的money字段是double类型,最终导致拷贝失败,而这位差点被开除的程序员恰好是这种场景。KXV28资讯网——每日最新资讯28at.com

基于上述描述,BeanUtils.copyProperties无法在编译期间对拷贝字段的修改及时感知错误,假如公司上线规范不严,或者回归测试不全面,一旦出现上述字段名称或者类型被修改,很大可能造成线上问题,所以需要慎用BeanUtils.copyProperties。KXV28资讯网——每日最新资讯28at.com

三、替代方案

既然BeanUtils.copyProperties拷贝存在上述问题,那么,有没有什么好的替代方案呢?KXV28资讯网——每日最新资讯28at.com

有,通常替代方案有 2种:使用原始的setter和getter方法 和 MapStruct。KXV28资讯网——每日最新资讯28at.com

1.原始的setter和getter

使用原始的setter和getter方法进行拷贝,虽然会编写一些看似啰嗦的代码,但是它具备以下优点:KXV28资讯网——每日最新资讯28at.com

  • 控制的粒度更细,更灵活
  • 性能比BeanUtils.copyProperties的反射更高效
  • 如果拷贝字段有名称和类型更改或者setter和getter方法丢失,编译期立马能发现
  • 如下示例,可以将多个Source的字段按需拷贝到Target上:
import java.util.UUID;public Target convetSourceToTarget(Source1 source1, Source2 source2) {    Target target = new Target();    target.setId(UUID.randomUUID().toString());    target.setName(source1.getName());    target.setAge(source1.getAge());    target.setAddress(source2.getAddress());}

2.MapStruct

(1) 使用示例KXV28资讯网——每日最新资讯28at.com

MapStruct是一个很优秀的 Java库,也是用于简化对象之间的拷贝工作,其主要特点如下:KXV28资讯网——每日最新资讯28at.com

  • 编译时生成代码:MapStruct在编译时生成映射代码,避免了运行时的性能开销
  • 类型安全:生成的代码是类型安全的,编译时即可发现映射错误
  • 易于使用:通过注解配置,使用简单直观

为了更好地说明 MapStruct,我们以一个示例进行说明:KXV28资讯网——每日最新资讯28at.com

首先,我们需要增加mapstruct的依赖:KXV28资讯网——每日最新资讯28at.com

// maven 依赖<dependency>    <groupId>org.mapstruct</groupId>    <artifactId>mapstruct</artifactId>    <version>1.5.2.Final</version></dependency>// gradle依赖implementation 'org.mapstruct:mapstruct:1.5.2.Final'

然后,定义一个Mapper接口:KXV28资讯网——每日最新资讯28at.com

import org.mapstruct.Mapper;import org.mapstruct.Mapping;import org.mapstruct.factory.Mappers;@Mapperpublic interface TestMapper {    TestMapper INSTANCE = Mappers.getMapper(TestMapper.class);    /**     * 在Mapping中定义对象的 source和 target字段,     * 如果source和 target的类型不一样,编译期会报错    */    @Mapping(source = "name", target = "fullName")     UserDTO toDTO(UserEntity entity);}

接着,定义两个实体类:KXV28资讯网——每日最新资讯28at.com

public class UserDTO {    private String fullName;    private int age;}public class UserEntity {    private String name;    private int age;}

最后,写一个测试类:KXV28资讯网——每日最新资讯28at.com

public class MapStructTest {    public static void main(String[] args) {        UserEntity entity = new UserEntity();        entity.setName("John");        entity.setAge(30);        UserDTO dto = TestMapper.INSTANCE.toDTO(entity);        System.out.println(dto.getFullName()); // 输出: John        System.out.println(dto.getAge());  // 输出: 30    }}

上述代码,在编译器会自动创建一个TestMapperImpl实现类,如下图:KXV28资讯网——每日最新资讯28at.com

KXV28资讯网——每日最新资讯28at.com

(2) 实现原理KXV28资讯网——每日最新资讯28at.com

最后,总结下MapStruct实现原理:KXV28资讯网——每日最新资讯28at.com

① 注解处理器机制KXV28资讯网——每日最新资讯28at.com

MapStruct使用了 Java的注解处理器机制,通过实现javax.annotation.processing.Processor接口,在编译时扫描和处理特定的注解。KXV28资讯网——每日最新资讯28at.com

② 注解扫描与处理KXV28资讯网——每日最新资讯28at.com

MapStruct定义了@Mapper、@Mapping 等注解,编译器会调用注解处理器来处理这些注解。KXV28资讯网——每日最新资讯28at.com

③ 代码生成KXV28资讯网——每日最新资讯28at.com

MapStruct会根据注解信息,解析源类和目标类的结构,并生成相应的映射,大致有以下几个步骤:KXV28资讯网——每日最新资讯28at.com

  • 解析注解和类结构:MapStruct 解析@Mapper接口、方法签名以及@Mapping注解,获取源类和目标类的字段信息。
  • 生成映射方法:根据解析结果,生成具体的映射方法,并调用源类的getter方法获取值并赋值给目标类的对应字段。
  • 处理复杂映射:对于嵌套对象、集合等复杂结构,MapStruct会递归生成相应的映射代码

④ 类型安全与错误检查KXV28资讯网——每日最新资讯28at.com

在代码生成过程中,MapStruct会进行类型检查,确保源字段和目标字段的类型匹配,如果发现类型不匹配会报编译时错误。KXV28资讯网——每日最新资讯28at.com

⑤ 支持自定义KXV28资讯网——每日最新资讯28at.com

MapStruct允许用户自定义映射逻辑,比如下面的示例,通过qualifiedByName和 @Named注解实现了一个自定义的方法:KXV28资讯网——每日最新资讯28at.com

@Mapping(target = "tags", source = "tagSet", qualifiedByName = "defaultToEmptySet")UserEntity fromDO(UserDTO dto);@Named("defaultToEmptySet")default Set<String> defaultToEmptySet(Set<String> items) {    return items == null ? new LinkedHashSet<>() : items;}

四、如何选择?

原始的setter和getter方法简单且灵活,mapstruct通过注解的方式,比起原始的setter和getter门槛会高一点。KXV28资讯网——每日最新资讯28at.com

两种方式都是编译行为,因此,一旦拷贝的字段发生改变能及时感知,对程序员比较友好。KXV28资讯网——每日最新资讯28at.com

具体如何选择,可以根据团队约定而定,如果是个人学习,优先推荐mapstruct,可以作为一个学习和实践点。KXV28资讯网——每日最新资讯28at.com

五、总结

本文通过分析BeanUtils.copyProperties的源码,总结了它的几个缺点,综合评估,建议慎用!KXV28资讯网——每日最新资讯28at.com

接着,通过分析mapstruct的原理以及使用案例,它完美解决了BeanUtils.copyProperties的缺点,是对象拷贝很不错的选择。KXV28资讯网——每日最新资讯28at.com

对于原始的setter和getter也是对象拷贝很不错的选择。KXV28资讯网——每日最新资讯28at.com

温馨建议:如果使用三方的工具类,一定要事先了解其优缺点和安全性问题,这样才能在使用过程中能做到心中有谱,处事不乱,避免拆盲盒导致不必要的事故。如果有更多的精力,再去研究下其原理,吸收他人优秀的思维。KXV28资讯网——每日最新资讯28at.com

本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-92151-0.html为什么不推荐使用 BeanUtils.copyProperties?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Python 函数三剑客 reduce、filter &amp; map

下一篇: 万字聊一聊RocketMQ一条消息短暂而又精彩的一生

标签:
  • 热门焦点
  • K60 Pro官方停产 第三方瞬间涨价

    K60 Pro官方停产 第三方瞬间涨价

    虽然没有官方宣布,但Redmi的一些高管也已经透露了,Redmi K60 Pro已经停产且不会补货,这一切都是为了即将到来的K60 Ultra铺路,属于厂家的正常操作。但有意思的是该机在停产之后
  • 一加Ace2 Pro真机揭晓 钛空灰配色质感拉满

    一加Ace2 Pro真机揭晓 钛空灰配色质感拉满

    终于,在经过了几波预热之后,一加Ace2 Pro的外观真机图在网上出现了。还是博主数码闲聊站曝光的,这次的外观设计还是延续了一加11的方案,只是细节上有了调整,例如新加入了钛空灰
  • 5月iOS设备好评榜:iPhone 14仅排第43?

    5月iOS设备好评榜:iPhone 14仅排第43?

    来到新的一月,安兔兔的各个榜单又重新汇总了数据,像安卓阵营的榜单都有着比较大的变动,不过iOS由于设备的更新换代并没有那么快,所以相对来说变化并不大,特别是iOS好评榜,老款设
  • JavaScript 混淆及反混淆代码工具

    JavaScript 混淆及反混淆代码工具

    介绍在我们开始学习反混淆之前,我们首先要了解一下代码混淆。如果不了解代码是如何混淆的,我们可能无法成功对代码进行反混淆,尤其是使用自定义混淆器对其进行混淆时。什么是混
  • 三万字盘点 Spring 九大核心基础功能

    三万字盘点 Spring 九大核心基础功能

    大家好,我是三友~~今天来跟大家聊一聊Spring的9大核心基础功能。话不多说,先上目录:图片友情提示,本文过长,建议收藏,嘿嘿嘿!一、资源管理资源管理是Spring的一个核心的基础功能,不
  • 超闭合精工铰链 彻底消灭缝隙 三星Galaxy Z Flip5与Galaxy Z Fold5发布

    超闭合精工铰链 彻底消灭缝隙 三星Galaxy Z Flip5与Galaxy Z Fold5发布

    2023年7月26日,三星电子正式发布了Galaxy Z Flip5与Galaxy Z Fold5。三星新一代折叠屏手机采用超闭合精工铰链,让折叠后的缝隙不再可见。同时,配合处
  • iQOO Neo8 Pro真机谍照曝光:天玑9200+和V1+旗舰双芯加持

    iQOO Neo8 Pro真机谍照曝光:天玑9200+和V1+旗舰双芯加持

    去年10月,iQOO推出了iQOO Neo7系列机型,不仅搭载了天玑9000+,而且是同价位唯一一款天玑9000+直屏旗舰,一经上市便受到了用户的广泛关注。在时隔半年后,
  • 华为举行春季智慧办公新品发布会 首次推出电子墨水屏平板

    华为举行春季智慧办公新品发布会 首次推出电子墨水屏平板

    北京时间2月27日晚,华为在巴塞罗那举行春季智慧办公新品发布会,在海外市场推出之前已经在中国市场上市的笔记本、平板、激光打印机等办公产品,并首次推出搭载
  • 苹果MacBook Pro 2021测试:仍不支持平滑滚动

    苹果MacBook Pro 2021测试:仍不支持平滑滚动

    据10月30日9to5 Mac 消息报道,苹果新的 14 英寸和 16 英寸 MacBook Pro 2021 上市后获得了不错的评价,亮点包括行业领先的性能,令人印象深刻的电池续航,精美丰
Top
Baidu
map