热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

浅析Java中ApacheBeanUtils和SpringBeanUtils的用法

这篇文章主要介绍了Java中ApacheBeanUtils和SpringBeanUtils的用法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

# 前言

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

# 对象拷贝

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

# 什么是浅拷贝和深拷贝

在Java中,除了 基本数据类型之外,还存在 类的实例对象这个引用数据类型,而一般使用 “=”号做赋值操作的时候,对于基本数据类型,实际上是拷贝的它的值,但是对于对象而言,其实赋值的只是这个对象的引用,将原对象的引用传递过去,他们实际还是指向的同一个对象。

而浅拷贝和深拷贝就是在这个基础上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。

简单来说:

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

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

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

# 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);
}

但是由于 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 beans
  if (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 necessary
  if (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 
   Map propMap = (Map) orig;
   for (final Map.Entry 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<&#63;> editable,
  @Nullable String... ignoreProperties) throws BeansException {

 Assert.notNull(source, "Source must not be null");
 Assert.notNull(target, "Target must not be null");

 Class<&#63;> 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 ignoreList = (ignoreProperties != null &#63; 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等等

到此这篇关于浅析Java中Apache BeanUtils和Spring BeanUtils的用法的文章就介绍到这了,更多相关Apache BeanUtils和Spring BeanUtils内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • Docker的安全基准
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 本文探讨了Hive中内部表和外部表的区别及其在HDFS上的路径映射,详细解释了两者的创建、加载及删除操作,并提供了查看表详细信息的方法。通过对比这两种表类型,帮助读者理解如何更好地管理和保护数据。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 网络运维工程师负责确保企业IT基础设施的稳定运行,保障业务连续性和数据安全。他们需要具备多种技能,包括搭建和维护网络环境、监控系统性能、处理突发事件等。本文将探讨网络运维工程师的职业前景及其平均薪酬水平。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • 三星W799在2011年的表现堪称经典,以其独特的双屏设计和强大的功能引领了双模手机的潮流。本文详细介绍其配置、功能及锁屏设置。 ... [详细]
  • 本文总结了汇编语言中第五至第八章的关键知识点,涵盖间接寻址、指令格式、安全编程空间、逻辑运算指令及数据重复定义等内容。通过详细解析这些内容,帮助读者更好地理解和应用汇编语言的高级特性。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 数据管理权威指南:《DAMA-DMBOK2 数据管理知识体系》
    本书提供了全面的数据管理职能、术语和最佳实践方法的标准行业解释,构建了数据管理的总体框架,为数据管理的发展奠定了坚实的理论基础。适合各类数据管理专业人士和相关领域的从业人员。 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • 优化版Windows 10 LTSC 21H2企业版:适用于低内存设备
    此版本为经过优化的Windows 10 LTSC 21H2企业版,特别适合低内存配置的计算机。它基于官方版本进行了精简和性能优化,确保在资源有限的情况下依然能够稳定运行。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 当iOS设备越狱后,某些插件可能会导致系统崩溃(白苹果)。此时,可以通过进入安全模式来排查并删除有问题的插件。本文将详细介绍如何通过特定按键组合进入不加载MobileSubstrate的安全模式,并提供相关背景知识。 ... [详细]
  • 使用C#开发SQL Server存储过程的指南
    本文介绍如何利用C#在SQL Server中创建存储过程,涵盖背景、步骤和应用场景,旨在帮助开发者更好地理解和应用这一技术。 ... [详细]
author-avatar
v敏0敏v_405_961
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有