慎用BeanUtils,性能真的拉跨

news2024/9/27 17:29:58

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>类型。(搜索公众号Java知音,回复“2021”,送你一份Java面试题宝典)

我们看下编译生成的 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 {

    @Override
    public 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

@Override
public 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 的效率也是非常高的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/139848.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

xxx.nginx转发+OpenResty(nginx升级版)_web服务器+lua

看上图&#xff0c;点开图发现图片空白不是网卡了&#xff0c;是内容有点多&#xff0c;缩小图片&#xff08;放大指定位置&#xff09;后食用。&#x1f601;内容分为三部分&#xff1a;nginx转发OpenResty(nginx升级版)_web服务器lua测试1.nginx转发1.1.搭建nginx 略&#xf…

【B样条曲线:计算系数】

对这篇文章的翻译&#xff0c;仅学习。 B样条曲线&#xff1a;计算系数 尽管de Boor的算法是计算b样条曲线上对应于给定u的点的标准方法&#xff0c;但我们在许多情况下确实需要这些系数(例如&#xff0c;曲线插值和逼近)。我们将举例说明一种简单的方法。 给定一条由n1个控制…

【PyQt】PyQt的缺陷及意外退出的可能原因

1 背景 最近在软件项目中比较深入的用到了 Qt GraphicsView Framework。PyQt 作为 Qt 的非官方Python绑定库&#xff08;Qt的官方Python绑定库是PySide&#xff09;总是出现很多意外崩溃/Bug&#xff0c;并且很难调试和追踪。 2 问题 软件项目中需要自定义 QGraphicsItem &a…

初识Cocos Creator

1.1 Cocos 不同时期与产品刚接触Cocos家族的时候&#xff0c;会有很多个Cocos的版本与分支&#xff0c;比如Cocos2d, Cocos2d-x, Cocos Creator 1.x, Cocos Creator 2.x, Cocos Cretor3D, Cocos Creator 3.x, CocosDashboard, 等我们先把Cocos 的主要产品脉络梳理一遍。对啦&am…

人体reid数据集

mars数据集 参考&#xff1a;https://blog.csdn.net/qq_34132310/article/details/83869605 整个Mars数据的大小大概有6.3G。 bbox_train文件夹中&#xff0c;有625个子文件夹&#xff08;代表着625个行人id&#xff09;&#xff0c;共包含了8298个小段轨迹&#xff08;track…

联合证券|战略新兴产业火了,高增长低估值股曝光

导读&#xff1a;2022年&#xff0c;A股商场值得记载的前史性大事件不断。这一年&#xff0c;A股商场上市公司数量正式打破5000家&#xff0c;战略新兴产业上市公司数量打破2500家&#xff0c;占比初次打破50%大关。这一年&#xff0c;A股商场顶住杂乱的外部环境因素&#xff0…

Pytorch介绍与基本使用

前言Pytorch是torch的python版本&#xff0c;是由Facebook开源的神经网络框架&#xff0c;专门针对 GPU 加速的深度神经网络&#xff08;DNN&#xff09;编程&#xff0c;根据我在网上的了解&#xff0c;相比于Tensorflow&#xff0c;Pytorch简介易用。一、为什么选择Pytorch简…

在VBA里使用range(cells(),cells()) ,一个容易出错的点:需要注意 range() 和cells() 前都指定好sheet名!

1 问题 在VBA里使用range(cells(),cells()) 发现处了下面这个状况 当前sheet就是sh1时&#xff0c;代码正常运行当前sheet不是sh1时&#xff0c;有一行代码报错 arr3 sh1.Range(Cells(4, 3), Cells(16, 8))为什么会这样呢&#xff1f;Sub test1001()Dim sh1 As ObjectSet s…

足球视频AI(二)——球员与球的目标检测

一、基础概念 1.1 识别目标&#xff1a; 1&#xff09;固定机位的视频中球员逐帧识别 2&#xff09;固定机位的视频中球逐帧识别 3&#xff09;位置换算与记录 1.2 实现思路 1&#xff0c;利用OpenCV的相邻帧差异识别移动物体 2&#xff0c;利用YOLO7的机器学习识别对象&…

LNPS递送辅料,DOPE,4004-05-1,二油酰磷脂酰乙醇胺

LNPS递送辅料&#xff0c;DOPE&#xff0c;4004-05-1&#xff0c;二油酰磷脂酰乙醇胺 中文名称 &#xff1a;1,2-二油酰-sn-甘油-3-磷酸乙醇胺 中文别称 &#xff1a;二油酰磷脂酰乙醇胺 英 文 名 &#xff1a;DOPE 英文别称 &#xff1a;1,2-Dioleoyl-sn-glycero-3-phospho…

数据仓库介绍

数据仓库数据仓库的概念数据仓库的主要特征数据仓库的主流开发语言-sql结构化数据sql语句数据仓库的概念 数据仓库&#xff08;英语&#xff1a;Data Warehouse&#xff0c;简称数仓、DW&#xff09;,是一个用于存储、分析、报告的数据系统。 数据仓库的目的是构建面向分析的集…

一、MyBatis-Plus简介

文章目录一、MyBatis-Plus简介1、简介2、特性3、支持数据库4、框架结构5、代码及文档地址【尚硅谷】MyBatisPlus教程-讲师&#xff1a;杨博超 失败&#xff0c;是正因你在距成功一步之遥的时候停住了脚步。 一、MyBatis-Plus简介 1、简介 MyBatis-Plus&#xff08;简称 MP&am…

吴恩达【神经网络和深度学习】Week2——神经网络基础

文章目录1、Logistic Regression as a Neural Network1.1、Binary Classification1.1.1、Introduction1.1.2、Notations1.2、Logistic Regression1.3、Logistic Regression Cost Function1.4、Gradient Descent1.5、Derivatives&#xff08;导数&#xff09;1.6、More derivati…

基于支持向量回归(SVR)和PROSAIL模拟光谱数据的叶面积指数反演

前言本博客利用PROSAIL模型模拟出MODIS的光谱数据和LAI&#xff0c;然后采用支持向量回归&#xff08;SVR&#xff09;方法建立NDVI与LAI 的回归模型&#xff0c;用于LAI的反演。训练和测试数据的拟合效果还是比较好的&#xff0c;这表明SVR在模拟的光谱数据与LAI回归方面的可行…

NXP EMDA学习(2):串口eDMA接收和发送流程详解

在单片机中&#xff0c;最基础的一个驱动就是串口&#xff0c;本文就以NXP中串口eDMA的收发为例&#xff0c;通过分析源代码来理解eDMA的执行过程。 参考代码&#xff1a;Kinetis K64 Sub-Family SDK 2.11中的uart_edma_transfer.c 文章目录1 串口基本初始化2 DMAMUX初始化3 初…

Go语言 WaitGroup 源码知多少

前面的文章我们写协程的时候有用到 WaitGroup 我们的写法大概是这样的 func main() {...dothing()wg : sync.WaitGroup{}// 控制 多个子协程的声明周期wg.Add(xx)for i : 0; i < xx; i {go func(ctx context.Context) {defer wg.Done()...dothing()}(ctx)}...dothing()// …

【Android春招】Android基础

一、填空题 1&#xff0e;Android是基于__ 的移动端开源操作系统。 Linux 2&#xff0e;Android系统是由__公司推出的。 谷歌 3&#xff0e;Android 11对应的API编号是__。 30 4&#xff0e;App除了在手机上运行&#xff0c;还能在电脑的__上运行。 模拟器&#xff08;AVD&…

Pytorch SoftMax回归

目录 数据集 从零实现 简单实现 数据集 导入所需库 torchvision计算机视觉所用torch的库 %matplotlib inline import torch import torchvision from torch.utils import data from torchvision import transforms from d2l import torch as d2l # 用SVG清晰度高 d2l.use_…

华为交换机OSPF对接思科交换机EIGRP,牛逼配置!

简介 思科交换机OSPF分别与华为交换机OSPF以及思科交换机EIGRP进行路由交互&#xff0c;间接实现华为交换机OSPF对接思科交换机EIGRP的功能。 配置注意事项 该案例适用于支持OSPF的华为交换机。该案例仅提供OSPF对接EIGRP的基本配置。思科交换机与华为交换机对接替换时&…

固定资产年结操作步骤 及常见问题处理:AJAB 关闭资产年度报错问题处理

目录 第一步、打开新的资产会计年度 TCODE &#xff1a; AJRW 第二步、 关闭资产年度 TCODE&#xff1a;AJAB 三、结果校验 四、 常见问题 分析 第一步、打开新的资产会计年度 TCODE &#xff1a; AJRW 输入公司编码&#xff0c;输入新的资产会计年度 先测试运行&a…