两难!到底用Apache BeanUtils还是Spring BeanUtils?

news/2024/7/5 3:16:49

点击上方“方志朋”,选择“设为星标”

回复”666“获取新整理的面试文章

在我们实际项目开发过程中,我们经常需要将不同的两个对象实例进行属性复制,从而基于源对象的属性信息进行后续操作,而不改变源对象的属性信息,比如DTO数据传输对象和数据对象DO,我们需要将DO对象进行属性复制到DTO,但是对象格式又不一样,所以我们需要编写映射代码将对象中的属性值从一种类型转换成另一种类型。

对象拷贝

在具体介绍两种 BeanUtils 之前,先来补充一些基础知识。它们两种工具本质上就是对象拷贝工具,而对象拷贝又分为深拷贝和浅拷贝,下面进行详细解释。

什么是浅拷贝和深拷贝

在Java中,除了 基本数据类型之外,还存在 类的实例对象这个引用数据类型,而一般使用 “=”号做赋值操作的时候,对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际还是指向的同一个对象。而浅拷贝和深拷贝就是在这个基础上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。

简单来说:

  • 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝

  • 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

BeanUtils

前面简单讲了一下对象拷贝的一些知识,下面就来具体看下两种 BeanUtils 工具

Apache 的 BeanUtils

首先来看一个非常简单的BeanUtils的例子

publicclass PersonSource {private Integer id;private String username;private String password;private Integer age;// getters/setters omiited
}
publicclass PersonDest {private Integer id;private String username;private Integer age;// getters/setters omiited
}
publicclass TestApacheBeanUtils {public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {//下面只是用于单独测试PersonSource personSource = new PersonSource(1, "pjmike", "12345", 21);PersonDest personDest = new PersonDest();BeanUtils.copyProperties(personDest,personSource);System.out.println("persondest: "+personDest);}
}
persondest: PersonDest{id=1, username='pjmike', age=21}

从上面的例子可以看出,对象拷贝非常简单,BeanUtils最常用的方法就是:

//将源对象中的值拷贝到目标对象//将源对象中的值拷贝到目标对象
public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {BeanUtilsBean.getInstance().copyProperties(dest, orig);
}

默认情况下,使用org.apache.commons.beanutils.BeanUtils对复杂对象的复制是引用,这是一种浅拷贝

但是由于 Apache下的BeanUtils对象拷贝性能太差,不建议使用,而且在阿里巴巴Java开发规约插件上也明确指出:

Ali-Check | 避免用Apache Beanutils进行属性的copy。

commons-beantutils 对于对象拷贝加了很多的检验,包括类型的转换,甚至还会检验对象所属的类的可访问性,可谓相当复杂,这也造就了它的差劲的性能,具体实现代码如下:

public void copyProperties(final Object dest, final Object orig)throws IllegalAccessException, InvocationTargetException {// Validate existence of the specified beansif (dest == null) {thrownew IllegalArgumentException("No destination bean specified");}if (orig == null) {thrownew IllegalArgumentException("No origin bean specified");}if (log.isDebugEnabled()) {log.debug("BeanUtils.copyProperties(" + dest + ", " +orig + ")");}// Copy the properties, converting as necessaryif (orig instanceof DynaBean) {final DynaProperty[] origDescriptors =((DynaBean) orig).getDynaClass().getDynaProperties();for (DynaProperty origDescriptor : origDescriptors) {final String name = origDescriptor.getName();// Need to check isReadable() for WrapDynaBean// (see Jira issue# BEANUTILS-61)if (getPropertyUtils().isReadable(orig, name) &&getPropertyUtils().isWriteable(dest, name)) {final Object value = ((DynaBean) orig).get(name);copyProperty(dest, name, value);}}} elseif (orig instanceof Map) {@SuppressWarnings("unchecked")final// Map properties are always of type <String, Object>Map<String, Object> propMap = (Map<String, Object>) orig;for (final Map.Entry<String, Object> entry : propMap.entrySet()) {final String name = entry.getKey();if (getPropertyUtils().isWriteable(dest, name)) {copyProperty(dest, name, entry.getValue());}}} else/* if (orig is a standard JavaBean) */ {final PropertyDescriptor[] origDescriptors =getPropertyUtils().getPropertyDescriptors(orig);for (PropertyDescriptor origDescriptor : origDescriptors) {final String name = origDescriptor.getName();if ("class".equals(name)) {continue; // No point in trying to set an object's class}if (getPropertyUtils().isReadable(orig, name) &&getPropertyUtils().isWriteable(dest, name)) {try {final Object value =getPropertyUtils().getSimpleProperty(orig, name);copyProperty(dest, name, value);} catch (final NoSuchMethodException e) {// Should not happen}}}}}

Spring 的 BeanUtils

使用spring的BeanUtils进行对象拷贝:

publicclass TestSpringBeanUtils {public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {//下面只是用于单独测试PersonSource personSource = new PersonSource(1, "pjmike", "12345", 21);PersonDest personDest = new PersonDest();BeanUtils.copyProperties(personSource,personDest);System.out.println("persondest: "+personDest);}
}

Spring下的BeanUtils也是使用 copyProperties方法进行拷贝,只不过它的实现方式非常简单,就是对两个对象中相同名字的属性进行简单的get/set,仅检查属性的可访问性。具体实现如下:

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) {Method writeMethod = targetPd.getWriteMethod();if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());if (sourcePd != null) {Method readMethod = sourcePd.getReadMethod();if (readMethod != null &&ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {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);}}}}}}

可以看到,成员变量赋值是基于目标对象的成员列表,并且会跳过ignore的以及在源对象中不存在,所以这个方法是安全的,不会因为两个对象之间的结构差异导致错误,但是必须保证同名的两个成员变量类型相同

小结

以上简要的分析两种BeanUtils,因为Apache下的BeanUtils性能较差,不建议使用,可以使用 Spring的BeanUtils ,或者使用其他拷贝框架,比如:Dozer、ModelMapper等等

来源 | https://urlify.cn/vUfIry

热门内容:选型必看:RabbitMQ 七战 Kafka,差异立现为什么阿里规定需要在事务注解@Transactional中指定rollbackFor?
一套简单通用的Java后台管理系统,拿来即用,非常方便(附项目地址)半吊子架构师,一来就想干掉RabbitMQ ...
前、后端分离权限控制设计和实现思路最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
明天见(。・ω・。)ノ♡

http://lihuaxi.xjx100.cn/news/272448.html

相关文章

多相机视觉系统的坐标系统标定与统一及其应用

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶”重磅干货&#xff0c;第一时间送达导语&#xff1a;随着机器视觉应用的日益广泛&#xff0c;大幅面 多相机 视觉系统的需求越来越多&#xff0c;主要应用方向为大幅面高精度的定位与测量和场景拼接等。多相…

Google重磅发布开源库TFQ,快速建立量子机器学习模型

整理 | 弯月编辑 | 郭芮出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;近日&#xff0c;Google 与滑铁卢大学、大众汽车等联合发布 TensorFlow Quantum&#xff08;TFQ&#xff09;&#xff0c;一个可快速建立量子机器学习模型原型的开源库。TFQ提供了必…

如何防止我的模型过拟合?这篇文章给出了6大必备方法

选自Medium作者&#xff1a;Mahitha Singirikonda机器之心编译编辑&#xff1a;小舟正如巴菲特所言&#xff1a;「近似的正确好过精确的错误。」在机器学习中&#xff0c;过拟合&#xff08;overfitting&#xff09;会使模型的预测性能变差&#xff0c;通常发生在模型过于复杂的…

YUV和RGB格式分析

做嵌入式项目的时候&#xff0c;涉及到YUV视频格式到RGB图像的转换&#xff0c;虽然之前有接触到RGB到都是基于opencv的处理&#xff0c;很多东西并不需要我们过多深入的去探讨&#xff0c;现在需要完全抛弃现有的算法程序&#xff0c;需要从内存中一个字节一个字节的处理&…

用Python做一个游戏辅助脚本,完整编程思路分享

一、说明 简述&#xff1a;本文将以4399小游戏《 宠物连连看经典版2 》作为测试案例&#xff0c;通过识别小图标&#xff0c;模拟鼠标点击&#xff0c;快速完成配对。对于有兴趣学习游戏脚本的同学有一定的帮助。 运行环境&#xff1a;Win10/Python3.5。 主要模块&#xff1…

可编辑表格的实现

表格是网站中最常见的数据表现形式之一&#xff0c;不过大部分表格里数据都是从后台获取后直接展示的。本文将介绍可编辑表格的实现。 自适应宽高的多行输入 多行输入最常用的标签就是textarea&#xff0c;textarea正常使用的话是固定宽高的&#xff0c;内容超出的情况下默认是…

推荐:一款Java开源的Springboot 即时通讯 IM 聊天系统

点击上方“方志朋”&#xff0c;选择“设为星标”回复”666“获取新整理的面试文章# 开篇电商平台最不能缺的就是即时通讯&#xff0c;例如通知类下发&#xff0c;客服聊天等。今天&#xff0c;就来给大家分享一个开源的即时通讯系统。如对文章不感兴趣可直接跳至文章末尾&…

win7x64注册表显卡渲染速度_Geek3D GpuTest GUI(显卡测试软件)下载-Geek3D GpuTest GUI(显卡测试软件)免费版下载v0.7.0...

Geek3D GpuTest GUI 能够对显卡的GPU、OpenGL进行基准压力的测试&#xff0c;而且测试完成也相当的快捷&#xff0c;功能强大&#xff0c;体积小巧&#xff0c;目前支持在windows 64位系统上进行测试&#xff0c;帮助用户检测显卡的型号&#xff0c;让用户更好的使用显卡并发挥…