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

深入解析hutool的BeanUtil.copyProperties在多线程环境下的潜在陷阱

1. 为什么CopyOnWriteArrayList会变成ArrayList这个问题困扰了我整整两天。当时生产环境突然报出ArrayIndexOutOfBoundsException异常查看日志发现是在ArrayList.add方法抛出的但明明代码里用的是CopyOnWriteArrayList啊这种线程安全集合怎么会突然变成非线程安全的ArrayList呢经过仔细排查发现问题出在BeanUtil.copyProperties这个方法上。我们先来看个简单的复现代码Getter Setter class SourceDTO { private ListString dataList new CopyOnWriteArrayList(); } Getter Setter class TargetDTO { private ListString dataList; } public void testCopy() { SourceDTO source new SourceDTO(); source.getDataList().add(test); TargetDTO target new TargetDTO(); BeanUtil.copyProperties(source, target); // 这里target.dataList实际上变成了ArrayList System.out.println(target.getDataList().getClass()); // 输出java.util.ArrayList }这个现象很有意思明明源对象用的是CopyOnWriteArrayList复制后却变成了ArrayList。这背后的原因要从Hutool的类型转换机制说起。2. Hutool的类型转换机制解析Hutool在复制属性时会执行类型检查和转换。关键代码在Convert.convertWithCheck方法中public static Object convertWithCheck(Type targetType, Object value, Object defaultValue, boolean ignoreError) { if (null value) { return defaultValue; } return ConverterRegistry.getInstance().convert(targetType, value, defaultValue); }当遇到集合类型时会调用CollectionConverter进行处理。问题就出在CollUtil.create方法public static T CollectionT create(Class? collectionType) { if (collectionType.isAssignableFrom(ArrayList.class)) { return new ArrayList(); // 这里固定创建ArrayList } // 其他集合类型的处理... }这个方法有个设计特点当目标类型是List接口时默认创建ArrayList实例。这就是为什么我们的CopyOnWriteArrayList会被转换成ArrayList的根本原因。3. 多线程环境下的风险这种隐式的类型转换在多线程环境下尤其危险。来看个实际案例class TaskService { private ExecutorService executor Executors.newFixedThreadPool(4); public void processBatch(ListSourceDTO sources) { ListCompletableFutureVoid futures new ArrayList(); for (SourceDTO source : sources) { futures.add(CompletableFuture.runAsync(() - { TargetDTO target new TargetDTO(); BeanUtil.copyProperties(source, target); // 多线程操作转换后的集合 for (int i 0; i 100; i) { target.getDataList().add(value_ i); // 这里可能抛出ArrayIndexOutOfBoundsException } }, executor)); } CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); } }这个例子中原本线程安全的CopyOnWriteArrayList被转换成了非线程安全的ArrayList在多线程并发修改时就会出现数组越界异常。这种问题往往在低并发测试时难以发现但在生产环境高并发场景下就会暴露出来。4. 解决方案与最佳实践经过多次踩坑我总结了几个可行的解决方案4.1 使用自定义类型转换器// 注册自定义集合转换器 ConverterRegistry.getInstance().putCustom(List.class, (type, value, defaultValue) - { if (value instanceof CopyOnWriteArrayList) { return new CopyOnWriteArrayList((Collection?) value); } return new ArrayList((Collection?) value); }); // 或者更通用的处理方式 ConverterRegistry.getInstance().putCustom(List.class, (type, value, defaultValue) - { if (value instanceof Collection) { try { // 保持原集合类型 return value.getClass().getConstructor(Collection.class) .newInstance((Collection?) value); } catch (Exception e) { return new ArrayList((Collection?) value); } } return new ArrayList(); });4.2 使用属性复制忽略策略BeanUtil.copyProperties(source, target, CopyOptions.create().setIgnoreProperties(dataList)); // 然后手动处理集合属性 if (source.getDataList() ! null) { target.setDataList(new CopyOnWriteArrayList(source.getDataList())); }4.3 使用深度克隆替代属性复制// 使用Hutool的序列化克隆 TargetDTO target JSONUtil.toBean(JSONUtil.toJsonStr(source), TargetDTO.class); // 或者使用Java原生序列化 TargetDTO target CloneSupport.clone(source);在实际项目中我推荐方案4.1的自定义转换器方式它既能保持代码简洁又能确保类型安全。方案4.2虽然明确但比较繁琐适合对特殊字段需要特殊处理的场景。方案4.3的性能开销较大适合对性能要求不高的场景。5. 其他需要注意的陷阱除了集合类型转换问题在使用BeanUtil.copyProperties时还需要注意泛型类型擦除如果集合带有泛型类型复制后泛型信息会丢失循环引用问题对象之间存在循环引用时可能导致栈溢出final字段处理无法复制final修饰的字段静态字段处理默认不会复制静态字段性能考虑反射操作相比直接赋值有一定性能开销特别是在多线程环境下建议在使用前做好充分的测试包括高并发压力测试长时间运行测试边界条件测试6. 源码级别的深度优化如果项目中对属性复制的性能要求很高可以考虑基于Hutool源码进行定制化改造。关键点在于修改BeanCopier和CollectionConverter的实现// 自定义BeanCopier public class SafeBeanCopierT extends BeanCopierT { Override protected void beanToBean(Object source, Object target) { // 添加类型安全检查 if (source instanceof Collection target instanceof Collection) { Collection? srcCol (Collection?) source; Collection? destCol (Collection?) target; if (srcCol.getClass() ! destCol.getClass()) { // 保持集合类型一致 Collection? newCol createSameTypeCollection(srcCol); newCol.addAll(srcCol); BeanUtil.setFieldValue(target, dataList, newCol); return; } } super.beanToBean(source, target); } private Collection? createSameTypeCollection(Collection? sample) { try { return sample.getClass().getDeclaredConstructor().newInstance(); } catch (Exception e) { return new ArrayList(); } } }这种深度定制需要谨慎操作建议在充分理解Hutool源码的基础上进行并且做好充分的单元测试。7. 实际项目中的经验分享在金融项目中我们曾经因为这个问题导致对账系统在月末高峰期出现数据不一致。后来我们采用了组合方案对于核心业务对象使用自定义的SafeBeanUtils工具类在工具类中预注册常用的集合类型转换器对复制操作添加了监控和日志在CI/CD流程中加入集合类型安全检查public class SafeBeanUtils { static { // 预注册常用集合类型 registerCollectionConverter(CopyOnWriteArrayList.class); registerCollectionConverter(ConcurrentLinkedQueue.class); // ...其他线程安全集合 } private static void registerCollectionConverter(Class? collectionClass) { ConverterRegistry.getInstance().putCustom(collectionClass, (type, value, defaultValue) - { try { return collectionClass.getConstructor(Collection.class) .newInstance((Collection?) value); } catch (Exception e) { throw new RuntimeException(Failed to create collection, e); } }); } public static void copyProperties(Object source, Object target) { // 添加监控点 long start System.currentTimeMillis(); try { BeanUtil.copyProperties(source, target); } finally { long cost System.currentTimeMillis() - start; if (cost 100) { LOG.warn(Bean copy cost too much time: {}ms, cost); } } } }这套方案实施后再没出现过类似的线程安全问题。监控系统还能帮助我们及时发现性能热点优化业务代码。

相关文章:

深入解析hutool的BeanUtil.copyProperties在多线程环境下的潜在陷阱

1. 为什么CopyOnWriteArrayList会变成ArrayList? 这个问题困扰了我整整两天。当时生产环境突然报出ArrayIndexOutOfBoundsException异常,查看日志发现是在ArrayList.add方法抛出的,但明明代码里用的是CopyOnWriteArrayList啊!这种…...

Sunshine 完全卸载与系统清理指南

Sunshine 完全卸载与系统清理指南 【免费下载链接】Sunshine Sunshine: Sunshine是一个自托管的游戏流媒体服务器,支持通过Moonlight在各种设备上进行低延迟的游戏串流。 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine 引言 Sunshine作为一款…...

基于计算机网络原理优化LiuJuan模型分布式集群部署方案

基于计算机网络原理优化LiuJuan模型分布式集群部署方案 最近和几个做AI服务的同行聊天,大家普遍有个头疼的问题:模型单机部署,用户一多就卡死;想搞分布式集群,又怕架构太复杂,运维成本上天。这让我想起了之…...

手把手教程:用AI股票分析师daily_stock_analysis一键生成专业投资报告

手把手教程:用AI股票分析师daily_stock_analysis一键生成专业投资报告 你是不是也对那些动辄几十页、充满专业术语的股票分析报告感到头疼?自己研究吧,时间不够;找人分析吧,成本太高。现在,有个工具能让你…...

ADRC实战:用Python从零搭建一阶系统自抗扰控制器(附完整代码)

ADRC实战:用Python从零搭建一阶系统自抗扰控制器(附完整代码) 控制工程领域一直在追求更鲁棒、更智能的算法来应对复杂系统中的不确定性。自抗扰控制(Active Disturbance Rejection Control, ADRC)作为一种不依赖精确模…...

LibreELEC新手必看:用PVR IPTV Simple Client搞定电视直播(附免费m3u8源)

LibreELEC电视直播实战指南:从零搭建稳定流畅的IPTV系统 第一次在树莓派上打开央视高清频道时,那种用开源软件替代广电机顶盒的成就感至今难忘。LibreELEC作为专为Kodi优化的轻量级系统,配合PVR IPTV Simple Client插件,确实能打造…...

避坑指南:Unity触发器(Trigger)的5个典型误用场景与正确解决方案

Unity触发器(Trigger)实战避坑指南:5个高频误用场景与优化方案 在Unity物理交互开发中,触发器(Trigger)就像一把双刃剑——用得巧妙可以创造丝滑的游戏体验,用错地方则会导致诡异的bug和性能灾难。本文将揭示那些连资深开发者都可能踩中的陷阱…...

MedGemma医疗助手实战:从部署到问诊,小白也能用的AI医生

MedGemma医疗助手实战:从部署到问诊,小白也能用的AI医生 1. 引言:您的私人医疗AI助手 当深夜突然出现不明症状,或是阅读病历遇到难懂的医学术语时,您是否希望有个随时待命的专业医疗顾问?MedGemma医疗助手…...

douyin-downloader:突破平台限制的视频高效获取解决方案

douyin-downloader:突破平台限制的视频高效获取解决方案 【免费下载链接】douyin-downloader 项目地址: https://gitcode.com/GitHub_Trending/do/douyin-downloader 在数字内容快速迭代的时代,视频资源的高效获取面临平台访问限制、动态签名验证…...

VibeVoice模型推理加速:TensorRT优化实战

VibeVoice模型推理加速:TensorRT优化实战 1. 为什么VibeVoice需要TensorRT加速 VibeVoice作为微软推出的前沿语音合成模型,能生成长达90分钟的多角色自然对话,但它的计算复杂度也相当可观。我在实际部署时发现,直接用PyTorch运行…...

Meta-Llama-3-8B-Instruct零基础部署:5分钟用vLLM+Open WebUI搭建对话机器人

Meta-Llama-3-8B-Instruct零基础部署:5分钟用vLLMOpen WebUI搭建对话机器人 1. 准备工作:了解你的工具 Meta-Llama-3-8B-Instruct是Meta公司最新开源的80亿参数对话模型,相比前代产品,它在指令遵循、多轮对话和代码理解方面都有…...

MySQL连接查询实战:从头歌平台案例学多表联合查询技巧

MySQL连接查询实战:从头歌平台案例学多表联合查询技巧 在数据库应用开发中,多表联合查询是每个开发者必须掌握的核心技能。想象一下,当你需要从学生表中获取姓名,同时从成绩表中查询对应分数,再关联课程表获取课程名称…...

ComfyUI低显存模式避坑指南:如何正确使用--disable-cuda-malloc和--normalvram参数

ComfyUI低显存GPU优化实战:参数调优与性能平衡指南 当你在4GB显存的显卡上运行ComfyUI时,是否经常遇到RuntimeError: CUDA error: operation not supported的报错?这可能是显存管理策略与你的硬件不兼容导致的。本文将带你深入理解ComfyUI的显…...

3步解锁图像数据:让科研图表开口说话

3步解锁图像数据:让科研图表开口说话 【免费下载链接】WebPlotDigitizer Computer vision assisted tool to extract numerical data from plot images. 项目地址: https://gitcode.com/gh_mirrors/web/WebPlotDigitizer 在科研分析、工程计算和商业决策中&a…...

AI辅助开发:借助快马平台为你的网盘注入智能文件摘要与语义搜索能力

最近在捣鼓一个网盘项目,想着怎么让它更“聪明”一点。传统的网盘就是个文件仓库,找东西全靠文件名,有时候文件多了,或者名字起得随意,找起来真是费劲。正好看到大家都在玩AI,我就琢磨着,能不能…...

Qwen3-14b_int4_awq惊艳效果:输入‘画一个架构图:用户登录流程’生成PlantUML代码

Qwen3-14b_int4_awq惊艳效果:输入"画一个架构图:用户登录流程"生成PlantUML代码 1. 模型简介 Qwen3-14b_int4_awq是基于Qwen3-14b模型的int4量化版本,采用AngelSlim技术进行压缩优化,专门用于高效文本生成任务。这个量…...

Qwen3-14b_int4_awqvLLM部署详解:engine_args配置、tokenizer路径指定与量化权重加载

Qwen3-14b_int4_awq LLM部署详解:engine_args配置、tokenizer路径指定与量化权重加载 1. 模型简介 Qwen3-14b_int4_awq是基于Qwen3-14b模型的int4量化版本,采用AngelSlim技术进行压缩优化,专门用于高效文本生成任务。这个量化版本在保持模型…...

Matlab中如何灵活定制坐标轴标签:深入解析set(gca,xtick)与set(gca,xticklabel)

1. 为什么需要定制坐标轴标签? 在数据可视化过程中,默认的坐标轴标签往往不能满足我们的需求。比如绘制一个正弦函数时,Matlab会自动生成均匀分布的刻度值,但这些数值可能并不直观。想象一下,如果你要给非技术背景的同…...

SpringBoot+Vue3无人机AI巡检:从实时流处理到智能预警的闭环实践

1. 项目背景与技术选型 最近几年无人机巡检在安防、电力、农业等领域快速普及,但很多团队在落地时都会遇到视频延迟高、AI识别不准、预警响应慢等问题。去年我们团队用SpringBootVue3完整实现了一套无人机AI巡检系统,实测在2km范围内能做到500ms以内的端…...

3步激活旧Mac潜能:OpenCore Legacy Patcher让不支持的设备重获新生

3步激活旧Mac潜能:OpenCore Legacy Patcher让不支持的设备重获新生 【免费下载链接】OpenCore-Legacy-Patcher 体验与之前一样的macOS 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher OpenCore Legacy Patcher(OCLP&…...

如何用动态深度学习提升锂电池故障检测准确率?清华团队最新研究实践

动态深度学习在锂电池故障检测中的突破性实践 电动汽车的普及让锂电池安全问题日益凸显。传统检测方法在面对复杂工况时,往往表现出高误报率或漏检率,而清华大学团队的最新研究为这一难题提供了创新解决方案——通过动态深度学习技术,实现了锂…...

Aria2配置避坑指南:从自启动到浏览器插件联调(附完整.conf文件)

Aria2配置避坑指南:从自启动到浏览器插件联调(附完整.conf文件) 在Windows环境下配置Aria2自启动并实现浏览器插件联调,看似简单却暗藏诸多细节陷阱。许多用户在完成基础配置后,常遇到服务静默崩溃、RPC连接失败或下载…...

手把手教你修复libgit2报错:从corrupted loose reference到完整恢复Git仓库

手把手教你修复libgit2报错:从corrupted loose reference到完整恢复Git仓库 当你正在专注地开发项目,突然遇到corrupted loose reference file: refs/heads/master这样的Git错误时,那种感觉就像是在高速公路上突然爆胎。这个错误不仅会中断你…...

百度网盘下载加速:突破限速的高效解决方案

百度网盘下载加速:突破限速的高效解决方案 【免费下载链接】pan-baidu-download 百度网盘下载脚本 项目地址: https://gitcode.com/gh_mirrors/pa/pan-baidu-download 面对百度网盘动辄几十KB的下载速度,你是否也曾经历过等待大文件传输完成的煎熬…...

基于立创逻辑派与高云FPGA的100MHz双通道数字示波器DIY全解析

基于立创逻辑派与高云FPGA的100MHz双通道数字示波器DIY全解析 最近有不少朋友问我,想深入学习FPGA和嵌入式系统,有没有什么能动手又有挑战性的项目?今天我就来分享一个自己刚做完的“大玩具”——一个基于立创逻辑派开发板和高云FPGA的100MH…...

得物sign签名逆向避坑指南:常见MD5加密错误及解决方案

得物sign签名逆向避坑指南:常见MD5加密错误及解决方案 在逆向分析领域,sign签名机制一直是开发者关注的焦点。得物作为国内领先的潮流电商平台,其sign签名算法采用了经典的MD5加密方式,但在实际逆向过程中,开发者常常会…...

Phi-3-vision-128k-instruct保姆级部署教程:开源多模态模型GPU算力优化实操

Phi-3-vision-128k-instruct保姆级部署教程:开源多模态模型GPU算力优化实操 1. 模型简介 Phi-3-Vision-128K-Instruct是一个轻量级的开源多模态模型,属于Phi-3模型家族的最新成员。这个模型特别适合处理需要同时理解文本和图像的复杂任务,比…...

Phi-3-vision-128k-instruct代码实例:自定义Chainlit UI实现多图批量问答

Phi-3-vision-128k-instruct代码实例:自定义Chainlit UI实现多图批量问答 1. 模型简介 Phi-3-Vision-128K-Instruct是微软推出的轻量级多模态模型,属于Phi-3系列的最新成员。这个模型特别擅长处理图文结合的复杂任务,支持长达128K的上下文理…...

XTDrone--解决roslaunch px4 indoor1.launch依赖问题的实战指南

1. XTDrone环境搭建与依赖问题概述 第一次在XTDrone环境中运行roslaunch px4 indoor1.launch时,十有八九会遇到各种依赖报错。这就像组装一台新电脑,明明所有硬件都插好了,开机却总是提示缺少驱动。我刚开始接触XTDrone时,光是解决…...

SecGPT-14B企业级应用:与Jira/飞书打通,自动生成工单描述、复现步骤与修复方案

SecGPT-14B企业级应用:与Jira/飞书打通,自动生成工单描述、复现步骤与修复方案 1. 企业安全运维的痛点与解决方案 在当今企业IT环境中,安全团队每天需要处理大量安全工单,从漏洞报告到异常行为分析,再到修复方案制定…...