危险!属性拷贝工具的坑!
1. 背景
之前在专栏中讲过“不推荐使用属性拷贝工具”,推荐直接定义转换类和方法使用 IDEA 插件自动填充 get / set 函数。
不推荐的主要理由是:
- 有些属性拷贝工具性能有点差
- 有些属性拷贝工具有“BUG”
- 使用属性拷贝工具容易存在一些隐患(后面例子会讲到)
2. 示例
首先公司内部就遇到过 commons 包的 BeanUtils 进行属性拷贝性能较差的真实案例,然后该同事换成了 Spring 的 BeanUtils 性能好了很多,感兴趣大家可以使用性能测试框架或者基准测试框架去对比,这里就不对比了。
接下来我们看 Spring 的 BeanUtils 的属性拷贝会存在啥问题:
import lombok.Data;import java.util.List;@Data
public class A {private String name;private List<Integer> ids;
}
@Data
public class B {private String name;private List<String> ids;
}
import org.springframework.beans.BeanUtils;import java.util.Arrays;public class BeanUtilDemo {public static void main(String[] args) {A first = new A();first.setName("demo");first.setIds(Arrays.asList(1, 2, 3));B second = new B();BeanUtils.copyProperties(first, second);for (String each : second.getIds()) {// 类型转换异常System.out.println(each);}}
}
大家运行上述示例时,会发生类型转换异常。
打断点可以看到,属性拷贝之后 B 类型的 second 对象中 ids 仍然为 Integer 类型:

如果不转换为字符串,直接进行打印,并不会报错。
使用CGlib 在不定义Converter 的情况下也会遇到类似问题:
import org.easymock.cglib.beans.BeanCopier;import java.util.Arrays;public class BeanUtilDemo {public static void main(String[] args) {A first = new A();first.setName("demo");first.setIds(Arrays.asList(1, 2, 3));B second = new B();final BeanCopier beanCopier = BeanCopier.create(A.class, B.class, false);beanCopier.copy(first,second,null);for (String each : second.getIds()) {// 类型转换异常System.out.println(each);}}
}
同样,问题在运行时才暴露出来。
接下来我们看下 mapstruct:
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;@Mapper
public interface Converter {Converter INSTANCE = Mappers.getMapper(Converter.class);B aToB(A car);
}
import java.util.Arrays;public class BeanUtilDemo {public static void main(String[] args) {A first = new A();first.setName("demo");first.setIds(Arrays.asList(1, 2, 3));B second = Converter.INSTANCE.aToB(first);for (String each : second.getIds()) {// 正常System.out.println(each);}}
}
可以成功的将 A 中 List<Integer> 转为 B 中的 List<String> 类型。
我们看下编译生成的 Converter 实现类:
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.stereotype.Component;@Generated(value = "org.mapstruct.ap.MappingProcessor",comments = "version: 1.3.1.Final, compiler: javac, environment: Java 1.8.0_202 (Oracle Corporation)"
)
@Component
public class ConverterImpl implements Converter {@Overridepublic B aToB(A car) {if ( car == null ) {return null;}B b = new B();b.setName( car.getName() );b.setIds( integerListToStringList( car.getIds() ) );return b;}protected List<String> integerListToStringList(List<Integer> list) {if ( list == null ) {return null;}List<String> list1 = new ArrayList<String>( list.size() );for ( Integer integer : list ) {list1.add( String.valueOf( integer ) );}return list1;}
}
自动帮我们进行了转换,我们可能没有意识到类型并不一致。
如果我们在 A 类中添加一个 String number 属性,在 B 类中添加一个 Long number 属性,使用 mapstruect 当 number 设置为非数字类型时就会报 .NumberFormatException 。
@Overridepublic B aToB(A car) {if ( car == null ) {return null;}B b = new B();b.setName( car.getName() );if ( car.getNumber() != null ) { // 问题出在这里b.setNumber( Long.parseLong( car.getNumber() ) );}b.setIds( integerListToStringList( car.getIds() ) );return b;}
使用 cglib 默认则不会映射 number 属性,B 中的 number 为 null。
如果手动定义转换器,使用 IDEA 插件(如 generateO2O)自动转换:
public final class A2BConverter {public static B from(A first) {B b = new B();b.setName(first.getName());b.setIds(first.getIds());return b;}
}
在编码阶段就可以非常明确地发现这个问题:

3. 结论
由于 Java 的泛型其实是编译期检查,编译后泛型擦除,导致运行时 List<Integer> 和 List<String> 都是 List 类型,可以正常赋值。这就导致在使用很多属性映射工具时,编译时不容易明显的错误。
mapstruct 自定义了注解处理器,在编译阶段可以读取映射双方的泛型类型,进而进行映射。但是这种映射也很可怕,有时候我们由于粗心等原因定义错了类型,自动帮助我们进行了转换,会带了很多副作用。
之前对各种属性映射工具的性能进行了简单的对比,结果如下:

因此慎用属性转换工具,如果可能建议自定义转换类,使用 IDEA插件自动填充,效率也挺高, A 或 B 中任何属性类型不匹配,甚至删除一个属性,编译阶段即可报错,而且直接调用 get set 的效率也是非常高的。
相关文章:
危险!属性拷贝工具的坑!
1. 背景 之前在专栏中讲过“不推荐使用属性拷贝工具”,推荐直接定义转换类和方法使用 IDEA 插件自动填充 get / set 函数。 不推荐的主要理由是: 有些属性拷贝工具性能有点差有些属性拷贝工具有“BUG”使用属性拷贝工具容易存在一些隐患(…...
qt实现打开pdf(阅读器)功能用什么库比较合适
关于这个问题,网上搜一下,可以看到非常多的相关博客和例子,可以先看看这个总结性的博客(https://zhuanlan.zhihu.com/p/480973072) 该博客讲得比较清楚了,这里我再补充一下吧(qt官方也给出了一些…...
在node.js环境中使用web服务器http-server运行html静态文件
http-server http-server是一个超轻量级web服务器,它可以将任何一个文件夹当作服务器的目录供自己使用。 当我们想要在服务器运行一些代码,但是又不会配置服务器的时候,就可以使用http-server就可以搞定了。 使用方法 因为http-server需要…...
前端学习篇一(HTML)
Introduction ##文章内容:使用HBuilder制作一个简单的HTML5网页以此达到学习HTML5 的目的 ##编写内容:1.HTML实现平台 2.HTML简介 3.HTML语言解析 ##编写人:贾雯爽 ##最后更新时间:2024/07/01 Overview Details 一、HTML简介…...
VUE笔记
框架: 框架结构,把很多基础功能已经实现(封装了)。 框架:在基础语言之上,对各种基础功能进行封装,方便开发者,提高开发效率。 举例:操作页面 现在:点击按…...
Datawhale机器学习day-1
赛题 在当今科技日新月异的时代,人工智能(AI)技术正以前所未有的深度和广度渗透到科研领域,特别是在化学及药物研发中展现出了巨大潜力。精准预测分子性质有助于高效筛选出具有优异性能的候选药物。以PROTACs为例,它是…...
业务模型扩展字段存储
构建业务模型时,通常模型会设置扩展信息,存储上一般使用JSON格式存储到db中。JSON虽然有较好的扩展性,但并没有结构化存储的类型和非空等约束,且强依赖代码中写入/读取时进行序列化/反序列化操作, 当扩展信息结构简单且…...
50+k8s常用命令,助你成为k8s大牛!
Kubernetes是一个强大的容器编排平台,不管是运维、开发还是测试或多或少都会接触到,熟练的掌握k8s可大大提高工作效率和强化自身技能。 集群管理 1. 查看集群节点状态: kubectl get nodes2. 查看集群资源使用情况: kubectl top nodes3. 查看集群信息…...
002-基于Sklearn的机器学习入门:回归分析(上)
本节及后续章节将介绍机器学习中的几种经典回归算法,所选方法都在Sklearn库中聚类模块有具体实现。本节为上篇,将介绍基础的线性回归方法,包括线性回归、逻辑回归、多项式回归和岭回归等。 2.1 回归分析概述 回归(Regression&…...
python实现网页自动化(自动登录需要验证的网页)
引言: python作为实现网页自动化的一个重要工具,其强大的各种封装的库使得程序运行更加简洁,只需要下载相应的库,然后调用库中的函数就可以简便的实现我们想要的网页相关操作。 正文: 我的前几篇文章写了关于初学爬虫中比较容易上手的功能,例如爬取静态网页的数据、动…...
ctfshow-web入门-命令执行(web71-web74)
目录 1、web71 2、web72 3、web73 4、web74 1、web71 像上一题那样扫描但是输出全是问号 查看提示:我们可以结合 exit() 函数执行php代码让后面的匹配缓冲区不执行直接退出。 payload: cvar_export(scandir(/));exit(); 同理读取 flag.txt cinclud…...
一体化导航的优点及应用领域
一体化导航,作为现代导航技术的重要发展方向,正日益展现出其独特的魅力和广泛的应用前景。这种导航方式将多种导航技术、信息系统以及数据处理方法集成于一个统一的平台上,为用户提供高效、准确、便捷的导航服务。 一体化导航的核心在于其高度…...
“吃饭大学”!中国大学食堂排行TOP10(含西电)
同学们们,考研择校考虑的因素除了学术,地理位置等方面,你们还会考虑哪些因素呢?小研作为一个吃货,必定会考虑的一个因素当然是大学的食堂美食啊~ 那中国超级好吃的大学食堂在哪?一起来看看有没有你的目标院…...
使用 Mybatis 时,调用 DAO接口时是怎么调用到 SQL 的?
Mybatis 是一个流行的 Java 持久层框架,它提供了一种半自动的 SQL 映射方式,允许开发者在 Java 代码中以一种更加直观和灵活的方式来操作数据库。当你使用 Mybatis 调用 DAO 接口时,背后的工作流程大致如下: 接口定义:…...
微信小程序毕业设计-微信食堂线上订餐系统项目开发实战(附源码+论文)
大家好!我是程序猿老A,感谢您阅读本文,欢迎一键三连哦。 💞当前专栏:微信小程序毕业设计 精彩专栏推荐👇🏻👇🏻👇🏻 🎀 Python毕业设计…...
昂首资本实例使用价格行为策略,交易翻倍一点都不难
交易翻倍难吗?当Anzo Capital昂首资本使用价格行为策略进行交易时,发现一点都不难,以下是使用价格行为策略的实例分享: 1. 在初次交易信号出现时,推荐在1.00429价位入场,将止损设于1.04399,止盈…...
20240701 每日AI必读资讯
🏫AI真炼丹:整整14天,无需人类参与 - 英矽智能推出全球首个AI参与决策的生物学实验室,实现了14天内完成靶点发现和验证的全自动化闭环实验。 - 该实验室由PandaOmics平台驱动,集成多种预测模型和海量数据࿰…...
GPT-5 一年半后发布,对此你有何期待?
IT之家6月22日消息,在美国达特茅斯工程学院周四公布的采访中,OpenAI首席技术官米拉穆拉蒂被问及GPT-5是否会在明年发布,给出了肯定答案并表示将在一年半后发布。此外,穆拉蒂在采访中还把GPT-4到GPT-5的飞跃描述为高中生到博士生的…...
Redis学习——Redisson 分布式锁集成及其简单使用
文章目录 引言1. Redisson概述1.1 Redisson的基本概念1.2 Redisson的主要功能1.3 Redisson的优点 2. 开发环境3. Redisson的安装与配置3.1 添加依赖3.2 配置Redisson 4. 使用Redisson4.1 可重入锁4.1.1 可重入锁的概念4.1.2 可重入锁的实现原理4.1.3 简单使用锁的获取和释放 4.…...
08 - matlab m_map地学绘图工具基础函数 - 绘制线、图例、添加文字注释等函数
08 - matlab m_map地学绘图工具基础函数 - 绘制线、图例、添加文字注释等函数 0. 引言1. 关于m_line2. 关于m_quiver3. 关于m_text4. 关于m_plot5. 结语 0. 引言 本篇介绍下m_map中添加绘制基础线(m_line、m_plot)、绘制箭头(m_quiver&#x…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
鸿蒙HarmonyOS 5军旗小游戏实现指南
1. 项目概述 本军旗小游戏基于鸿蒙HarmonyOS 5开发,采用DevEco Studio实现,包含完整的游戏逻辑和UI界面。 2. 项目结构 /src/main/java/com/example/militarychess/├── MainAbilitySlice.java // 主界面├── GameView.java // 游戏核…...
