当前位置: 首页 > news >正文

BeanUtils.copyProperties浅拷贝的坑你得知道?

今天想写一篇文章,主要关于深拷贝和浅拷贝相关的,主要是最近写代码的时候遇到一个BUG,刚好涉及到浅拷贝导致的问题。

问题背景

现在有一个需要是需要修改门店信息,门店也区分父门店和子门店,父门店被编辑更新是需要通过到第三方的,然后之前是没有父子门店的概念的,后面新增的需求,然后editShop这个方法的入参就是关于门店的信息么,这里我简化它的参数,但是保留了一个data属于引用型参数。

下面的模拟当时出现BUG的代码

public class RequestDto {Map<String, Object> data = new HashMap<>();private Integer status ;private Long id;public Map<String, Object> getData() {return data;}
}public class ShopService {public void editShop(RequestDto requestDto) {System.out.println("入参:" + requestDto.toString());// 操作// 父门店获取子门店,假设有两个List<Long> childShops = getChildShop(requestDto.getId());childShops.stream().forEach(childShop -> {RequestDto requestDto1 = new RequestDto();BeanUtils.copyProperties(requestDto, requestDto1);requestDto1.setId(childShop);requestDto1.getData().put("isSync", Boolean.FALSE);editShop(requestDto1);sync(childShop);});System.out.println("返回前:" + requestDto.toString());if (Boolean.FALSE.equals(requestDto.getData().get("isSync"))) {return;}sync(requestDto.getId());}private List<Long> getChildShop(Long parentId) {if (parentId.equals(1L)) {// 父门店List<Long> childShopIds = new ArrayList<>();childShopIds.add(123L);childShopIds.add(234L);return childShopIds;} else {return new ArrayList<>();}}private void sync(Long shopId) {System.out.println("同步门店第三方:" + shopId);}
}
public class Main {public static void main(String[] args) {ShopService shopService = new ShopService();RequestDto requestDto = new RequestDto();requestDto.setId(1L);requestDto.setStatus(0);shopService.editShop(requestDto);}}

模拟了当时操作父门店时,需要触发子门店的配置信息同步,通过递归来实现似乎是不错的选择,但是将isSync作为递归结束的标志来,然后就可以完成配置的复制和更新,看起来是没有什么问题的,但是当这个代码真的到达线上的时候,发现编辑父门店,子门店触发通过不了,但是父门店就触发不了同步了,在判断的if里面就退出了,导致线上发现父门店配置没有同步的BUG。然后经过一顿头发输出,看了好几遍日志之后,也没有发现问题的所在,后面本地起了一个调试才发现,这个问题是出自于BeanUtils.copyProperties的问题,排查出来了。

BeanUtils.copyProperties的时候,已经将requestDto1中data的引用指向了requestDto的引用,然后对它添加isSync的时候是会导致父门店的dto也同时被修改了。

在这里插入图片描述

BeanUtils.copyProperties的源码分析

private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,@Nullable String... ignoreProperties) throws BeansException {Assert.notNull(source, "Source must not be null");Assert.notNull(target, "Target must not be null");Class<?> actualEditable = target.getClass();if (editable != null) {if (!editable.isInstance(target)) {throw new IllegalArgumentException("Target class [" + target.getClass().getName() +"] not assignable to Editable class [" + editable.getName() + "]");}actualEditable = editable;}// 获取所有的属性PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);// 遍历for (PropertyDescriptor targetPd : targetPds) {// 写入的set方法Method writeMethod = targetPd.getWriteMethod();if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {// source对象对应的的属性PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());if (sourcePd != null) {Method readMethod = sourcePd.getReadMethod();if (readMethod != null) {ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.boolean isAssignable =(sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ?ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :targetResolvableType.isAssignableFrom(sourceResolvableType));if (isAssignable) {try {// 设置成可访问,防止私有if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {readMethod.setAccessible(true);}// 赋值Object value = readMethod.invoke(source);if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {writeMethod.setAccessible(true);}writeMethod.invoke(target, value);}catch (Throwable ex) {throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", ex);}}}}}}}

然后这里我们可以看到它最后也是通过反射的invoke方法来进行set设置值,而value是从之前的source获取,所以获取的是它的引用,所以用的是浅引用。

浅拷贝和深拷贝

浅拷贝从上面的例子应该是非常容易就可以理解了,就是浅拷贝除了拷贝基础数据类型及其包装类型外,在对引用类型的拷贝时是直接拷引用,在Java中就是相当于是同一个的对象的引用。

深拷贝的话就是相当于创建一个新的一模一样的对象,但是这个对象的内存地址是不一致的。

在Java里面实现深拷贝的方式有三种。

  • 通过直接创建赋值,set

  • 通过序列化和反序列化创建的对象是属于重新生成的对象,也是属于深拷贝。

  • 通过fastjson这样的json反序列化也是实现深拷贝的一种方式。

  • 通过实现重写父类的clone方法也可以实现。

借此记录下自己踩到的BUG,虽然不是很难的东西,写出来也是希望下一次遇到这个问题的时候能够更快的解决!

相关文章:

BeanUtils.copyProperties浅拷贝的坑你得知道?

今天想写一篇文章&#xff0c;主要关于深拷贝和浅拷贝相关的&#xff0c;主要是最近写代码的时候遇到一个BUG&#xff0c;刚好涉及到浅拷贝导致的问题。 问题背景 现在有一个需要是需要修改门店信息&#xff0c;门店也区分父门店和子门店&#xff0c;父门店被编辑更新是需要通过…...

ubuntu安装rabbitMQ 并 开启记录消息的日志

apt-get update apt-get install rabbitmq-server rabbitmqctl add_user root password // 设置用户名密码 rabbitmqctl set_user_tags root administrator // 设置为管理员身份 rabbitmqctl set_permissions -p / root ".*" ".*" ".*" //为…...

思维模型 首因效应

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。先入为主&#xff0c;一见钟情。 1 首因效应的应用 1.1 面试中的首因效应 小李是一名应届毕业生&#xff0c;他准备参加一家知名互联网公司的面试。在面试前&#xff0c;他做了充分的准备…...

Redis极速上手开发手册【Redis全面复习】

文章目录 什么是RedisRedis的特点Redis的应用场景Redis安装部署Redis基础命令Redis多数据库特性Redis数据类型Redis数据类型之stringRedis数据类型之hashRedis数据类型之listRedis数据类型之setRedis数据类型之sorted set案例&#xff1a;存储高一班的学员信息 Redis封装工具类…...

[动态规划] (十四) 简单多状态 LeetCode LCR 091.粉刷房子

[动态规划] (十四) 简单多状态 LeetCode LCR 091.粉刷房子 文章目录 [动态规划] (十四) 简单多状态 LeetCode LCR 091.粉刷房子题目解析解题思路状态表示状态转移方程初始化和填表顺序返回值 代码实现总结 LCR 091. 粉刷房子 题目解析 (1) 一排房子&#xff0c;共有n个 (2) 染…...

【VSS版本控制工具】

VSS版本控制工具 1 安装 VSS2 服务器端配置3 新建用户4 客户端配置Vss2005Vs20055 客户端详细操作 1 安装 VSS 第一步&#xff1a;将VisualSourceSafe2005安装包解压。 第二步&#xff1a;找到setup.exe双击运行。 第三步&#xff1a;在弹出的界面复选框中选中Iaccepttheterms…...

数据持久化技术(Python)的使用

传统数据库连接方式&#xff1a;mysql&#xff08;PyMySQL&#xff09;ORM 模型&#xff1a;SQLAlchemy MyBatis、 Hibernate PyMySQL 安装&#xff1a; pip install pymysql简单使用 利用 pymysql.connect 建立数据库连接并执行 SQL 命令&#xff08;需要提前搭建好数据库…...

第23章(上)_索引原理之索引与约束

文章目录 索引索引分类主键选择索引的代价 约束外键约束约束与索引的区别 索引使用场景不要使用索引的场景总结 索引 索引的概念&#xff1a;索引是一种有序的存储结构。索引按照单个或多个列的值进行排序。 索引的目的&#xff1a;提升搜索效率。 索引分类 按照数据结构分为…...

金蝶云星空BOS设计器中基础资料字段属性“过滤”设置获取当前界面的基础资料值作为查询条件

文章目录 金蝶云星空BOS设计器中基础资料字段属性“过滤”设置获取当前界面的基础资料值作为查询条件背景说明业务需求格式BOS配置 金蝶云星空BOS设计器中基础资料字段属性“过滤”设置获取当前界面的基础资料值作为查询条件 背景说明 序列号档案是基础资料&#xff0c;资料里…...

OFDM深入学习及MATLAB仿真

文章目录 前言一、OFDM 基本原理及概念1、OFDM 简介2、子载波3、符号4、子载波间隔与符号长度之间的关系 二、涉及的技术1、保护间隔2、交织3、信道编码4、扩频5、导频6、RF&#xff08;射频&#xff09;调制7、信道估计 三、变量间的关系四、IEEE 802.11a WLAN PHY 层标准五、…...

软件测试简历原来是写了这些才让面试官已读不回

前言&#xff1a; 马上就到了面试跳槽涨薪好时候了&#xff0c;最近看很多的小伙伴已经开始投简历了&#xff0c;一天投了几十次几百次&#xff0c;面试官已读不会&#xff0c;面试的机会都没有更别说后面的事情的&#xff0c;这是为什么呢&#xff1f; 很大一部分的原因是的…...

ESP32网络开发实例-Web服务器RGB LED调光

Web服务器RGB LED调光 文章目录 Web服务器RGB LED调光1、RGB LED介绍3、软件准备4、硬件准备4、代码实现在本文中,我们将创建一个 RGB LED 控制器网络服务器。 Web 服务器将显示用于设置 RGB LED 颜色的色谱。 颜色将主要分为三种:红色、绿色和蓝色。 用户将从光谱中选择一种…...

C# TCP Server服务端多线程监听RFID读卡器客户端上传的读卡数据

本示例使用设备介绍&#xff1a;液显WIFI无线网络HTTP协议RFID云读卡器可编程实时可控开关TTS语-淘宝网 (taobao.com) using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using Sy…...

【electron】【附排查清单】记录一次逆向过程中,fetch无法请求http的疑难杂症(net::ERR_BLOCKED_BY_CLIENT)

▒ 目录 ▒ &#x1f6eb; 导读需求开发环境 1️⃣ Adblock等插件拦截2️⃣ 【失败】Content-Security-Policy启动服务器json-serverhtml中的meta字段 3️⃣ 【失败】https vs httpwebPreferences & allowRunningInsecureContent disable-features 4️⃣ 【失败】检测fetch…...

【JS】scrollTop+scrollHeight+clientTop+clientHeight+offsetTop+offsetHeight

scrollTop、scrollHeight、clientTop、clientHeight、offsetTop以及offsetHeight 1. scrollTop 与 scrollHeight 1.1 scrollTop scrollTop 是这六个属性中唯一一个可写的属性。 Element.scrollTop 属性可以获取或设置一个元素的内容垂直滚动的像素数。 一个元素的 scrollT…...

Go语言函数用法

文章目录 Go语言函数用法 Go语言函数用法 函数在Go语言中有多种用法&#xff0c;它们是组织和模块化代码、提高代码的可维护性和可重用性的关键部分。以下是函数的一些常见用法&#xff1a; 封装代码&#xff1a;函数允许将一组相关的代码块封装到一个独立的单元中&#xff0c…...

3.5、Linux:命令行git的使用

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 在Linux Centos7.6下安装git yum -y install git 注册一个gitee账号 进去注册就好&#xff0c;记住自己的用户名和密码。 创建一个仓库 点击复制&#xff0c;接着就可以在Linux上使用了 git clone git clone 刚才复制的地…...

基于servlet+jsp+mysql网上书店系统

基于servletjspmysql网上书店系统 一、系统介绍二、功能展示四、其它1.其他系统实现五.获取源码 一、系统介绍 项目类型&#xff1a;Java web项目 项目名称&#xff1a;基于servletjspmysql网上书店系统 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 前端技…...

自用工具类整理

自动生成数据 uuid&雪花id private static Long workerId 1L; private static Long datacenterId 1L; private static Snowflake snowflake IdUtil.createSnowflake(workerId, datacenterId);public static String getId(String idType) {if (idType.equals("uui…...

jenkins2

jenkins插件管理安装&#xff1a;docker-build jenkins安装了docker 配置docke builder 添加 unix:///var/run/docker.sock rootubuntu20:~# usermod -G docker jenkins 修改docker中service文件添加 -H tcp://0.0.0.0:2376 jenkins中系统管理中 tcp://localhost:2376...

YOLOv5独家改进:分层特征融合策略MSBlock | 南开大学提出YOLO-MS |超越YOLOv8与RTMDet,即插即用打破性能瓶颈

💡💡💡本文独家改进:分层特征融合策略MSBlock,不同Kernel-Size卷积在不同尺度提升特征提取能力,最终引入到YOLOv5,做到二次创新 1)MSBlock使用;2)和C3结合使用 推荐指数:5颗星 MSBlock | 亲测在多个数据集能够实现大幅涨点,小目标检测效果也不错 💡💡…...

HTTP 协议详解-上(Fiddler 抓包演示)

文章目录 HTTP 协议HTTP 协议的工作过程HTTP 请求 (Request)认识URL关于 URL encode认识 "方法" (method)GET 方法POST 方法其他方法请求 "报头" (header)请求 "正文" (body) HTTP 响应详解状态码响应 "报头" (header) HTTP 协议 HTT…...

龙迅LT8911EXB功能概述 MIPICSI/DSI TO EDP

LT8911EXB 描述&#xff1a; Lontium LT8911EXB是MIPIDSI/CSI到eDP转换器&#xff0c;单端口MIPI接收器有1个时钟通道和4个数据通道&#xff0c;每个数据通道最大运行2.0Gbps&#xff0c;最大输入带宽为8.0Gbps。转换器解码输入MIPI RGB16/18/24/30/36bpp、YUV422 16/20/24bp…...

EtherCAT主站SOEM -- 5 -- SOEM之ethercatdc.h/c文件解析

EtherCAT主站SOEM -- 5 -- SOEM之ethercatdc.h/c文件解析 一 ethercatdc.h/c文件功能预览:二 ethercatdc.h/c 文件的主要函数的作用:2.1.1 函数:`ec_configdc()`2.1.2 函数:`ec_dcsync0(uint16 slave, boolean act, uint32 CyclTime, int32 CyclShift)`2.1.3 函数:`ec_dcs…...

【分布式事务】深入探索 Seata 的四种分布式事务解决方案的原理,优缺点以及在微服务中的实现

文章目录 前言一、XA 模式1.1 XA 模式原理1.2 XA 模式的优缺点及应用场景1.3 Seata XA 模式在微服务中的实现 二、AT 模式2.1 Seata AT 模式原理2.2 AT 模式的脏写问题和写隔离3.3 AT 模式的优缺点3.4 Seata AT 模式在微服务中的实现 三、TCC 模式3.1 TCC 模式原理3.2 Seata 的…...

C语言 || volatile

在C语言中&#xff0c;volatile是一个关键字&#xff0c;用于告诉编译器某个变量是易变的&#xff08;即可能会被程序以外的因素修改&#xff09;&#xff0c;从而告诉编译器不要对该变量进行优化&#xff0c;以确保程序的正确性。 volatile常用于以下几种情况&#xff1a; 并…...

网络安全之CSRF漏洞原理和实战,以及CSRF漏洞防护方法

一、引言 总体来说CSRF属于一种欺骗行为&#xff0c;是一种针对网站的恶意利用&#xff0c;尽管听起来像跨站脚本&#xff08;XSS&#xff09;&#xff0c;但是与XSS非常不同&#xff0c;并且攻击方式几乎向佐。XSS利用站点内的信任用户&#xff0c;而CSRF则通过伪装来自受信任…...

vivo 网络端口安全建设技术实践

作者&#xff1a;vivo 互联网安全团队 - Peng Qiankun 随着互联网业务的快速发展&#xff0c;网络攻击的频率和威胁性也在不断增加&#xff0c;端口是应用通信中的门户&#xff0c;它是数据进出应用的必经之路&#xff0c;因此端口安全也逐渐成为了企业内网的重要防线之一&…...

[ Linux Busybox ] flash_eraseall 命令解析

文章目录 相关结构体flash_eraseall 函数实现flash_eraseall 实现流程图 文件路径&#xff1a;busybox-1.20.2/miscutils/flash_eraseall.c 相关结构体 MTD 相关信息结构体 struct mtd_info_user {__u8 type; // MTD 设备类型__u32 flags; // MTD设…...

RabbitMQ 消息中间件 消息队列

RabbitMQ1、RabbitMQ简介2、RabbitMQ 特点3、什么是消息队列4、RabbiMQ模式5、集群中的基本概念 单实例安装RabbitMQ安装依赖安装erlang安装rabbitmq开启rabbitmq的web访问界面添加用户修改配置文件重启服务浏览器访问Rabbit-test rabbitMQ集群准备工作&#xff08;三台&#x…...