为什么说要慎用BeanUtils,因为性能真的拉跨
1 背景
之前在专栏中讲过“不推荐使用属性拷贝工具”,推荐直接定义转换类和方法使用 IDEA 插件自动填充 get / set 函数。
不推荐的主要理由是:
有些属性拷贝工具性能有点差
有些属性拷贝工具有“BUG”
使用属性拷贝工具容易存在一些隐患(后面例子会讲到)
2 示例
2.1 Spring 的 BeanUtils 的属性拷贝
首先公司内部就遇到过 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 类型:

如果不转换为字符串,直接进行打印,并不会报错。
2.2 CGlib 在不定义Converter 的情况
使用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);}}
}
同样,问题在运行时才暴露出来。
2.3 mapstruct
接下来我们看下 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。
2.4 手动定义转换器
如果手动定义转换器,使用 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 的效率也是非常高的。
相关文章:

为什么说要慎用BeanUtils,因为性能真的拉跨
1 背景之前在专栏中讲过“不推荐使用属性拷贝工具”,推荐直接定义转换类和方法使用 IDEA 插件自动填充 get / set 函数。不推荐的主要理由是:有些属性拷贝工具性能有点差有些属性拷贝工具有“BUG”使用属性拷贝工具容易存在一些隐患(后面例子…...

【项目设计】高并发内存池(六)[细节优化+测试]
🎇C学习历程:入门 博客主页:一起去看日落吗持续分享博主的C学习历程博主的能力有限,出现错误希望大家不吝赐教分享给大家一句我很喜欢的话: 也许你现在做的事情,暂时看不到成果,但不要忘记&…...

同模块设置不同应用主题方案
有时候公司内部会有不同应用但是有部分模块功能一样,只根据应用角色有些细节逻辑区分的场景。这时候往往采用模块化采用以应用至不同的APP。如果APP主题不一致,该如果解决。 方案: 在不同应用的config.gradle 下面根据不同应用定义不同的a…...

centos7 安装 hyperf
PHP > 7.4 Swoole PHP 扩展 > 4.5,并关闭了 Short Name OpenSSL PHP 扩展 JSON PHP 扩展 PDO PHP 扩展 Redis PHP 扩展 Protobuf PHP 扩展 composer create-project hyperf/hyperf-skeleton 推荐安装项 全部选n php.ini [swoole] extens…...

RZ/G2UL核心板-40℃低温启动测试
1. 测试对象HD-G2UL-EVM基于HD-G2UL-CORE工业级核心板设计,一路千兆网口、一路CAN-bus、 3路TTL UART、LCD、WiFi、CSI 摄像头接口等,接口丰富,适用于工业现场应用需求,亦方便用户评估核心板及CPU的性能。HD-G2UL-CORE系列工业级核…...

PyQt5可视化 7 饼图和柱状图实操案例 ①Qt项目的创建
目录 一、新建Qt项目 二、添加组件和布局 三、添加资源 1. 新建资源文件 2. 添加图标资源 四、frameHead 1. toolBtnGenData 2. toolBtnCounting 3. comboTheme 4. comboAnimation 5. Horizontal Spacer 6. toolBtnQuit 7. 设置toolBtnQuit的功能 8. frameHead的…...

0104路径搜索和单点路径-无向图-数据结构和算法(Java)
文章目录2 单点路径2.1 API2.2 算法实现后记2 单点路径 单点路径。给定一幅图和一个起点s,回答“从s到给定的目的顶点v是否存在一条路径?如果有,找出这条路径。”等类似问题。 2.1 API 单点路径问题在图的处理邻域中十分重要。根据标准设计…...

Maxscale读写分离实施文档
Maxscale介绍 MaxScale是maridb开发的一个mysql数据中间件,其配置简单,能够实现读写分离,并且可以根据主从状态实现写库的自动切换。 使用Maxscale无需对业务代码进行修改,其自带的读写分离模块,能够解析SQL语句&…...
websocket实现一个简单聊天框
websoket在客户端的使用 事件:open/message/error/close 方法:send/close var socket new WebSocket(url)// 服务连接成功时触发 socket.addEventListener(open, function() {console.log("连接成功了") })// 主动给websocket发消息 socket…...
Docker-安装应用
一、安装Tomcat 注意:新版Tomcat安装之后启动访问会出现404 修改:删除原有的webapps目录,修改webapps.dist为webapps 免修改版本:billygoo/tomcat8-jdk8 二、安装Mysql 1、安装 拉取镜像 docker pull mysql:5.7 运行镜像…...

Web3中的营销:如何在2023年获得优势
Mar. 2022, Daniel在过去的一年里,让人们对你的Web3项目或协议感兴趣已经变得越来越有挑战性。许多曾经充满希望的项目因为各种不同的原因,都在熊市中倒下了。然而,那些迄今为止幸存下来的项目都有一个共同点:强大的社区。Web3营销…...
Java中==和equals区别
文章目录Java中和equals区别1. Integer中和equals的问题1.1 Integer类型且不是通过new创建的比较1.2 手动new Integer()创建的比较1.3 Integer和int比较2. String中和equals的问题3. DemoJava中和equals区别 equals是方法,是运算符: 如果比较的对象是基…...

计算机科学导论笔记(三)
五、计算机组成 计算机组成部件可以分为三大类(子系统):中央处理单元(CPU)、主存储器和输入/输出子系统。 5.1 中央处理单元(CPU) 中央处理单元用于数据的运算,分为算术逻辑单元&a…...

Stream——数字类型的字符串排序
文章目录前言什么是数字类型的字符串一个简单的坑demo拯救坑代码对象集合中的数字类型排序(有坑)对象集合中的数字类型排序 解决扩展将数字类型字符串数组转换为Integer集合总结前言 想到给数据进行排序,一开始头脑中想到的就是sorted(),本篇文章重点说…...

.NET 8 预览版 1 发布!
.NET 8 是一个长期支持(LTS) 版本。这篇文章涵盖了推动增强功能优先级排序和选择开发的主要主题和目标。.NET 8 预览版和发布候选版本将每月交付一次。像往常一样,最终版本将在 11 月的某个时候在 .NET Conf 上发布。 .NET 版本包括产品、库、运行时和工具…...
WebGIS学习路线
7年经验的webgis码农在此文跟大家分享一些一路走来的所见所闻。希望能帮助刚刚跨入这个门槛的你。 入门之前我相信你已经搞清楚了以下几个问题: 1.什么是webgis? 2.webgis能够解决什么样的问题? 3.为什么你要学习webgis? 如果还没考虑清楚也没关系,可能你看完这篇文章…...
【独家】华为OD机试 - 停车场最大距离(C 语言解题)
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 最近更新的博客使用说明本期…...
12.typedef的使用与结构体定义
欢迎访问个人网络日志🌹🌹知行空间🌹🌹 文章目录1.基础介绍2.typedef 的常用的几种情况3.使用typedef可能出现的问题参考资料1.基础介绍 typedef是C/C语言中保留的关键字,用来定义一种数据类型的别名。 typedef并没有…...

宝塔+docker+jenkins部署vue项目(保姆级教程)
1.使用宝塔安装docker 在软件商城安装Docker管理器 2.使用docker下载jenkins镜像 使用命令行 docker pull jenkins/jenkins:lts //lts表示支持版本较长3.创建并且挂载jenkins目录并赋值 jenkins_home为我创建的目录 可以修改任意目录 mkdir -p /jenkins_home cho…...

JVM面试总结
1.java内存模型JMM是java的内存模型,JMM-也叫Java Memory Model,这里反应翻译成存储更好,因为工作内存指的不是内存.而是CPU寄存器,主内存才是内存.屏蔽了各种硬件和操作系统的内存访问差异-把硬件的细节封装起来,实现让java程序在各平台下都能达到一致的内存访问效果,它定义了…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...