Spring BeanUtils.copyProperties实现机制

server/2024/12/23 5:23:29/

Spring 框架中的 BeanUtils.copyProperties 方法提供了一种在两个 Java 对象之间复制属性的便捷方式。与 Apache Commons BeanUtils 类似,它也是基于反射来实现的。下面是关于其设计和实现的一些关键点:

设计思想

  1. 反射机制:同样依赖 Java 的反射机制,SpringBeanUtils 使用反射来获取对象的属性值并将其设置到另一个对象中。
  2. 灵活性:提供了一些选项来增加灵活性,比如忽略某些属性或仅复制特定类型的属性。
  3. 性能优化:相比于 Apache Commons BeanUtils,Spring 的实现更注重性能优化。

实现步骤

  1. 参数验证:检查源对象和目标对象是否为 null,以避免空指针异常。

  2. 属性获取

    • 利用 Java 的 Introspector 和 PropertyDescriptor 获取源对象和目标对象的所有属性。
    • 找到匹配的可读属性(getter 方法)和可写属性(setter 方法)。
  3. 属性匹配和复制

    • 遍历源对象的属性,并对每个属性执行以下操作:

      • 检查目标对象中是否存在同名且类型兼容的属性。
      • 如果存在,通过反射调用 getter 方法从源对象获取属性值,然后通过 setter 方法将该值设置到目标对象对应的属性上。
  4. 类型检查:确保只有当源和目标属性的类型兼容时才进行赋值,以防止类型不匹配的问题。

  5. 异常处理:捕获反射过程中可能发生的异常,如 IllegalAccessExceptionInvocationTargetException 等,并适当地处理这些异常。

注意事项

  • 浅拷贝BeanUtils.copyProperties 进行的是浅拷贝。如果属性是引用类型,复制的是引用而不是实际对象。
  • 忽略属性:方法允许指定要忽略的属性数组,从而不会复制这些属性。
  • 自定义转换器:Spring 的 BeanUtils 支持注册自定义属性编辑器,以支持更复杂的数据类型转换。
  • 性能考虑:由于会涉及反射,虽然经过优化,但在大量对象复制或频繁调用时仍需注意性能。
  • 嵌套属性:对于嵌套对象的属性,不会自动递归复制,需要手动执行属性复制操作。

核心代码示例

以下是简化后的核心代码逻辑示例:

java">public static void copyProperties(Object source, Object target) throws BeansException {// 参数验证if (source == null || target == null) {throw new IllegalArgumentException("Source and target must not be null");}// 获取源对象和目标对象的属性描述符PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(target.getClass());for (PropertyDescriptor targetPd : targetPds) {if (targetPd.getWriteMethod() != null) { // 目标属性必须有setterPropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());if (sourcePd != null && sourcePd.getReadMethod() != null) { // 源属性必须有gettertry {// 调用源对象的getter方法Method readMethod = sourcePd.getReadMethod();makeAccessible(readMethod);Object value = readMethod.invoke(source);// 调用目标对象的setter方法Method writeMethod = targetPd.getWriteMethod();makeAccessible(writeMethod);writeMethod.invoke(target, value);} catch (Throwable ex) {throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", ex);}}}}
}private static void makeAccessible(Method method) {if (!Modifier.isPublic(method.getDeclaringClass().getModifiers()) || !Modifier.isPublic(method.getModifiers())) {method.setAccessible(true);}
}

关键点分析

  • 反射机制:通过 Method.invoke() 调用 getter 和 setter,这是反射的典型应用。
  • 访问权限makeAccessible 方法确保即便属性或类不是公共的,也可以通过反射访问。
  • 异常处理:在反射操作中,一旦出现问题,比如访问权限、方法不存在等,会抛出异常,这里包装成 FatalBeanException 并重新抛出。

Apache Commons BeanUtils对比

Spring 的 BeanUtils.copyProperties 相比于 Apache Commons BeanUtils,确实在性能上做了一些优化。以下是分析两者之间的主要区别和优化点:

1. 属性获取方式

  • Apache Commons BeanUtils

    • 使用 PropertyUtils 来获取属性描述符,这一过程涉及较多的反射调用。
    • 其实现通常需要访问 DynaBean 和 Converter,增加了复杂性和额外的开销。
  • Spring BeanUtils

    • 使用 Java 内置的 IntrospectorPropertyDescriptor 来获取属性信息。这种方法相对轻量级,因为它不需要像 Apache Commons BeanUtils 那样通过 DynaBeans 等更复杂结构进行处理
    • 简化访问控制:Spring 在执行反射操作时,通过 ReflectionUtils.makeAccessible() 快速处理非公共方法的访问权限问题,而 Apache Commons BeanUtils 有时可能不得不采用更多的路径来处理类似问题

2. 缓存机制

  • Apache Commons BeanUtils

    • 缓存机制相对简单,主要依赖于 JVM 自身的类缓存。
    • 一些转换操作没有充分利用缓存。
  • Spring BeanUtils

    • 类元数据缓存:Spring 对类的 PropertyDescriptor 结果进行缓存,这意味着首次获取后,后续操作可以复用缓存的数据,减少了重复解析带来的开销。这个缓存通常实现为静态或线程安全的结构,以最大化复用效率。

3. 类型检查和转换

  • Apache Commons BeanUtils

    • 使用通用的 Converter 进行类型转换,灵活性高但性能略逊。
    • 检查和转换都比较泛化,可能导致一些不必要的性能损耗。
  • Spring BeanUtils

    • 提前验证:Spring 在复制前会确保源和目标属性之间的类型兼容性,避免不必要的反射失败尝试。这在很大程度上优化了性能,因为未通过检查的属性会被立即跳过,而不会尝试进行反射调用。
    • ConversionService 支持:Spring 提供灵活的 ConversionService 来支持属性值的自动类型转换,这意味着开发者可以通过配置减少手动转换的负担,从而提高整体性能

4. 异常处理

  • Apache Commons BeanUtils

    • 异常处理较为频繁,因为要兼容更多的场景和自定义行为。
    • 更多地使用受检异常,增加了性能开销。
  • Spring BeanUtils

    • 统一的异常策略:Spring 通过抛出 RuntimeException(如 FatalBeanException)来统一反射过程中遇到的各种异常。这种方式简化了代码逻辑,同时也提升了异常处理的性能,因为省去了捕获每个特定异常的开销。

5. 设计目标

  • Apache Commons BeanUtils

    • 设计上更加通用,为了适应更多的应用场景而牺牲了一定的性能。
    • 由于需要支持 DynaBean 和其他动态特性,增加了复杂度。
  • Spring BeanUtils

    • 目标明确,专注于高效、简单的 JavaBean 属性复制。
    • 更倾向于内省和反射技术的优化。

总的来说,Spring 的 BeanUtils.copyProperties 在性能优化上做了很多针对性的改进,如缓存机制、简化的反射操作、以及异常处理方面的提升。它的设计更贴近于实际的 JavaBean 操作,而 Apache Commons BeanUtils 则提供了更多样化的功能,适合需要更广泛支持的场景。

扩展:DynaBean分析

DynaBean 是 Apache Commons BeanUtils 库中的一个接口,用于创建动态 JavaBean。与传统 JavaBean 不同,DynaBean 不需要在编译时定义所有属性,它可以在运行时动态地添加和访问属性。这种机制为需要灵活处理不确定数据结构的场景提供了便利。

主要特点

  1. 动态属性管理

    • 可以在运行时添加、修改或删除属性,而不需要预先定义类中的字段。
  2. 灵活的数据结构

    • 提供了一种类似于映射(Map)或字典的方式来存储属性及其值,可以非常方便地操作不规则的数据。
  3. 接口方法

    • get(String name): 获取属性值。
    • set(String name, Object value): 设置属性值。
    • contains(String name): 检查是否存在某个属性。
    • remove(String name): 删除属性。

使用场景

  • 动态数据处理:在处理结构不固定的数据(如解析 XML/JSON 数据)时,可以使用 DynaBean 来简化代码。
  • 快速原型开发:当需要快速迭代和修改对象结构时,DynaBean 提供了极大的灵活性。
  • 表单和数据输入:在处理用户输入或表单数据时,可能会用到 DynaBean 来适配各种不同的输入格式。

总之,DynaBean 提供了一种灵活的方式来处理动态数据需求,在需要弹性数据结构的应用程序中非常有用


http://www.ppmy.cn/server/127712.html

相关文章

Vue之父尤雨溪成立VoidZero公告,已获得 460 万美元种子轮融资

VoidZero Inc. 创立公告 摘要: 我创立了 VoidZero Inc.,这是一家致力于构建开源、高性能、统一的开发工具链,服务于 JavaScript 生态系统的公司。我们已获得 Accel 领投的 460 万美元种子轮融资。 十五年前,当我开始使用 JavaSc…

栈的介绍与实现

一. 概念与结构 栈:⼀种特殊的线性表,其只允许在固定的⼀端进⾏插⼊和删除元素操作。进⾏数据插⼊和删除操作的⼀端称为栈顶,另⼀端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out的原则。 压栈:栈的插…

Qt 6 相比 Qt 5 的主要提升与更新

自从 Qt 6 发布以来,作为 Qt 框架的一个重大版本更新,它在多个核心方面进行了深度优化和改进。与 Qt 5 相比,Qt 6 不仅提升了性能,还改进了对现代硬件和图形 API 的支持,并增强了开发者的工作流程。本文将详细介绍 Qt …

YOLOv11目标检测实战2:人流统计、车流统计和跟踪(附源码)

目录 一、演示效果 二、基础理论和核心概念 三、安装环境和依赖 四、工作流程和步骤 五、核心部分源码: 六、总结 一、演示效果 二、基础理论和核心概念 YOLOv11 是 YOLO 系列的最新版本,它不仅在目标检测方面表现出色,还引入了对象分割和多目标跟踪的功能。本文将介绍…

Rust 语言开发 ESP32C3 并在 Wokwi 电子模拟器上运行(esp-hal 非标准库、LCD1602、I2C)

文章目录 esp-rs 简介GithubRust 包仓库Rust 教程Wokwi 电子模拟器开发环境Rust 环境esp-rs 环境创建 ESP32C3 项目项目结构编译项目命令运行模拟器ESP32C3 烧录 esp-rs 简介 esp-rs 是一个专注于为 Espressif 系列芯片(如 ESP32、ESP32-S2、ESP32-C3 等&#xff0…

DenseNet算法:口腔癌识别

本文为为🔗365天深度学习训练营内部文章 原作者:K同学啊 一 DenseNet算法结构 其基本思路与ResNet一致,但是它建立的是前面所有层和后面层的密集连接,它的另一大特色是通过特征在channel上的连接来实现特征重用。 二 设计理念 三…

《Linux从小白到高手》理论篇:深入理解Linux的网络管理

今天继续宅家,闲来无事接着写。本篇详细深入介绍Linux的网络管理。 如你所知,在Linux中一切皆文件。网卡在 Linux 操作系统中用 ethX,是由 0 开始的正整数,比如 eth0、eth1… ethX。而普通猫和ADSL 的接口是 pppX,比如 ppp0 等。 …

【Linux】第一个小程序——进度条实现

🔥 个人主页:大耳朵土土垚 🔥 所属专栏:Linux系统编程 这里将会不定期更新有关Linux的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉 文章目…