【JAVA基础】JAVA类的拷贝使用示例

文章目录

    • 一、框架介绍
    • 二、性能对比
    • 三、易用性对比
    • 四、使用示例
      • (一)Apache Commons BeanUtils 使用例子
        • 1、第一个例子:两个对象属性个数和名称一样,复制过程
        • 2、第二个例子:属性个数和名称不一样,复制过程
      • (二) MapStruct使用示例
    • 五、最终总结:

在这里插入图片描述

BeanUtils.copyProperties是Apache Commons
BeanUtils库中的一个方法,用于将一个Java对象的属性值复制到另一个Java对象中。这个方法简化了对象之间属性复制的过程,减少了手动编写逐个属性赋值的工作量,使得代码更加简洁、易于维护。

在Java开发中,我们经常需要将一个JavaBean对象的属性值复制到另一个JavaBean对象中,尤其是在数据转换、DTO(Data Transfer Object)转换等场景中。为了解决这个问题,本文将对这些框架进行性能对比,帮助开发者选择最适合自己项目的工具。

一、框架介绍

  • 1 、Apache Commons BeanUtils
    Apache Commons BeanUtils是一个用于操作JavaBean的工具类库,它提供了丰富的API来操作JavaBean的属性,包括属性拷贝。

  • 2 、Spring BeanUtils
    Spring框架自带的BeanUtils类也提供了属性拷贝的功能,通常与Spring框架一起使用。

  • 3 、ModelMapper
    ModelMapper是一个智能的对象映射库,它可以自动将一种类型的对象转换为另一种类型的对象,包括Bean的属性拷贝。

  • 4 、MapStruct
    MapStruct是一个基于注解的代码生成器,用于在Java Bean或POJO(Plain Old Java Object)之间进行映射。它生成的代码性能较高,但需要预先生成映射类。

二、性能对比

为了比较各框架的性能,我们进行了以下测试:

测试数据:创建一个包含10个属性的简单JavaBean对象。
测试方法:分别使用上述四个框架将源对象的属性值拷贝到目标对象中,记录每次操作所消耗的时间。
测试次数:对每个框架进行100万次测试,以减小误差。
测试结果如下:
在这里插入图片描述
从测试结果来看,MapStruct的性能最好,其次是Apache Commons BeanUtils和Spring BeanUtils,最后是ModelMapper。需要注意的是,这只是一个简单的测试,实际性能可能会因为具体的使用场景、对象属性数量、属性类型等因素而有所不同。

三、易用性对比

除了性能之外,易用性也是选择Bean属性拷贝工具类框架时需要考虑的因素。以下是各框架的易用性评价:

Apache Commons BeanUtils:API相对简单,但功能较为基础,对于复杂的映射场景可能需要手动处理。

Spring BeanUtils:与Spring框架集成紧密,适合在Spring项目中使用。但同样,对于复杂的映射场景可能需要额外处理。

ModelMapper:提供了丰富的配置选项,可以自动处理大部分映射场景。但在处理复杂类型或特殊需求时可能需要自定义配置。

MapStruct:需要预先生成映射类,对于大型项目来说可能需要花费一定的时间。但生成的代码性能较高,且支持自定义映射逻辑。

四、使用示例

(一)Apache Commons BeanUtils 使用例子

1、第一个例子:两个对象属性个数和名称一样,复制过程

在这个例子中,我们有两个类User和Person,它们的属性个数和名称都是一样的。我们可以使用BeanUtils.copyProperties方法将User对象的属性值复制到Person对象中。

import org.apache.commons.beanutils.BeanUtils;  @Data // 假设使用了Lombok的@Data注解来自动生成getter和setter  
public class User {  private String id;  private String name;  private String age; // 注意:这里使用String类型作为示例,实际中年龄可能用int或Integer更合适  
}  @Data // 假设使用了Lombok的@Data注解来自动生成getter和setter  
public class Person {  private String id;  private String name;  private String age; // 同样使用String类型作为示例  
}  public class Test {  public static void main(String[] args) {  User user = new User();  user.setId("1");  user.setName("John Doe");  user.setAge("30"); // 示例中使用String类型  Person person = new Person();  try {  BeanUtils.copyProperties(person, user); // 注意:这里源对象在前,目标对象在后  // 输出验证  System.out.println(person); // 输出:Person(id=1, name=John Doe, age=30)  } catch (Exception e) {  e.printStackTrace();  }  }  
}  // 注意:在实际使用中,如果源对象和目标对象的属性类型不一致但可以进行转换(如String到Integer),  
// 则BeanUtils会尝试进行类型转换。如果转换失败,将抛出异常。

**注意:**在上面的例子中,BeanUtils.copyProperties方法的参数顺序是目标对象在前,源对象在后,这与某些人的直觉可能相反。然而,这是BeanUtils库的设计方式。

2、第二个例子:属性个数和名称不一样,复制过程

当两个对象的属性个数和名称不一样时,BeanUtils.copyProperties方法只会复制那些名称相同且类型兼容的属性。对于名称不同或类型不兼容的属性,它们将不会被复制。

@Data  
public class SourceObject {  private String id;  private String userName; // 注意:这里属性名为userName  private int age; // 使用int类型  
}  @Data  
public class TargetObject {  private String id;  private String name; // 注意:这里属性名为name  private String age; // 使用String类型  private String email; // TargetObject特有的属性  
}  public class Test {  public static void main(String[] args) {  SourceObject source = new SourceObject();  source.setId("1");  source.setUserName("Jane Doe"); // 注意:这里设置的是userName  source.setAge(25); // 使用int类型  TargetObject target = new TargetObject();  try {  BeanUtils.copyProperties(source, target); // 尝试复制  // 输出验证  System.out.println(target); // 输出:TargetObject(id=1, name=null, age=null, email=null)  // 注意:由于name和userName名称不同,age类型不同,所以这些属性没有被复制  } catch (Exception e) {  e.printStackTrace();  }  }  
}

Apache Commons BeanUtils 处理属性名称不一致的情况:
如果需要在属性名称不一致的情况下进行属性复制,可以考虑以下几种方法:

  • 1、手动设置:直接通过目标对象的setter方法手动设置属性值。
    -2、使用Map作为中间对象:先将源对象的属性值复制到Map中,然后根据需要调整Map中的键名(即属性名),最后将Map中的值复制到目标对象中。
  • 3、自定义复制逻辑:编写自定义的方法来实现复杂的属性复制逻辑。
  • 4、使用Spring的BeanUtils或其他库:Spring的BeanUtils提供了更灵活的复制选项,包括忽略某些属性、处理嵌套对象等。但请注意,Spring的BeanUtils和Apache Commons BeanUtils是两个不同的库。
    注意:在实际应用中,如果两个对象的属性名称不一致,通常意味着它们在设计上代表了不同的概念或用途,因此可能需要重新考虑是否应该进行属性复制,或者是否应该通过其他方式来实现数据的传递和转换。

(二) MapStruct使用示例

  1. 引入依赖
    首先,你需要在项目的pom.xml文件中引入MapStruct的依赖。以下是一个示例依赖配置(注意版本可能会更新,请根据实际情况选择合适的版本):
<properties>  <mapstruct.version>1.5.5.Final</mapstruct.version> <!-- 请根据需要使用最新的稳定版本 -->  
</properties>  <dependencies>  <dependency>  <groupId>org.mapstruct</groupId>  <artifactId>mapstruct</artifactId>  <version>${mapstruct.version}</version>  </dependency>  <dependency>  <groupId>org.mapstruct</groupId>  <artifactId>mapstruct-processor</artifactId>  <version>${mapstruct.version}</version>  <scope>provided</scope> <!-- 设置为provided,因为注解处理器在编译时使用 -->  </dependency>  
</dependencies>
  1. 定义源类和目标类
    假设你有两个Java Bean类,一个是源类Person,另一个是目标类PersonDto。
public class Person {  private String firstName;  private String lastName;  // getters and setters  
}  public class PersonDto {  private String fullName;  // getters and setters  
}
  1. 创建Mapper接口
    然后,你需要创建一个Mapper接口,该接口定义了从源类到目标类的映射规则。
import org.mapstruct.Mapper;  
import org.mapstruct.Mapping;  
import org.mapstruct.factory.Mappers;  @Mapper  
public interface PersonMapper {  PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);  @Mapping(target = "fullName", expression = "java(person.getFirstName() + ' ' + person.getLastName())")  PersonDto personToPersonDto(Person person);  
}

在上面的Mapper接口中,@Mapper注解标记了这是一个MapStruct的Mapper接口。@Mapping注解定义了映射规则,其中target属性指定了目标类的属性名,expression属性则是一个Java表达式,用于定义如何从源类的属性计算得到目标类的属性值。

  1. 使用Mapper接口
    最后,你可以在代码中直接使用Mapper接口进行对象转换。
Person person = new Person();  
person.setFirstName("John");  
person.setLastName("Doe");  PersonDto personDto = PersonMapper.INSTANCE.personToPersonDto(person);  
System.out.println(personDto.getFullName()); // 输出: John Doe

在这个例子中,PersonMapper.INSTANCE.personToPersonDto(person)会自动将Person对象转换为PersonDto对象,并且根据@Mapping注解中定义的规则,将Person的firstName和lastName拼接后赋值给PersonDto的fullName属性。

使用注意:
MapStruct通过注解和代码生成的方式,提供了一种简洁、类型安全且高性能的对象转换方法。它特别适用于处理复杂的对象图映射,并且支持多种高级特性,如自定义映射方法、嵌套映射、集合映射等。在实际项目中,合理使用MapStruct可以显著提高开发效率和代码质量。

五、最终总结:

综合性能和易用性两方面的因素,开发者可以根据自己的项目需求选择合适的Bean属性拷贝工具类框架。对于追求极致性能的场景,MapStruct可能是一个不错的选择;而在简单的映射场景中,Apache Commons BeanUtils或Spring BeanUtils可能更加合适。对于需要自动处理复杂映射场景的开发者来说,ModelMapper可能是一个值得考虑的选项。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UnityHub下载任意版本的Unity包

1)先打开 // 也可以采用2直接打开 2)也可以直接打开 下载存档 (unity.com) 3)关联起来UnityHub即可

Mora:多智能体框架实现通用视频生成

人工智能咨询培训老师叶梓 转载标明出处 尽管已有一些模型能够生成视频&#xff0c;但大多数模型在生成超过10秒的长视频方面存在局限。Sora模型的出现标志着视频生成能力的一个新时代&#xff0c;它不仅能够根据文本提示生成长达一分钟的详细视频&#xff0c;而且在编辑、连接…

【CSS】定位

static ( 默认 )relative ( 相对定位 )absolute ( 绝对定位 )fixed ( 固定定位 )sticky ( 粘性定位 ) 普通文档流&#xff1f;浮动也会让元素脱离文档流&#xff0c;如果不设置浮动所有元素都处于普通文档流中。普通文档流中元素框的位置由元素在HTML中的位置决定&#xff0c;块…

Redisson分布式锁的概念和使用

Redisson分布式锁的概念和使用 一 简介1.1 什么是分布式锁&#xff1f;1.2 Redisson分布式锁的原理1.3 Redisson分布式锁的优势1.4 Redisson分布式锁的应用场景 二 案例2.1 锁竞争案例2.2 看门狗案例2.3 参考文章 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff…

文献阅读——基于拉格朗日乘子的电力系统安全域边界通用搜索方法

摘要 为提升电力系统安全域(security region&#xff0c;SR)的构建效 率&#xff0c;提出一种基于拉格朗日乘子(Lagrange multiplier&#xff0c;LM) 的电力系统安全域边界(security region boundary&#xff0c;SRB)通用搜索方法。 首先&#xff0c;根据电力系统静态安全性问…

2024.9.25 数据分析学习

资料&#xff1a; 【开课吧哩堂】数据挖掘项目之用户流失预警系统_哔哩哔哩_bilibili 五万字 | Spark吐血整理&#xff0c;学习与面试收藏这篇就够了&#xff01;-腾讯云开发者社区-腾讯云 (tencent.com) 黑马程序员Spark全套视频教程&#xff0c;4天spark3.2快速入门到精通…

文件上传漏洞+CTF实例

解题思路 前端绕过 手动修改前端js代码进行绕过&#xff1a;右击-查看页面源代码-ctff进行位置定位-修改JavaScript函数 后端绕过 文件类型绕过&#xff08;Content-Type&#xff09; 常见MIME类型描述application/octet-stream 表示所有其他情况的默认值 text/plain表示文…

啥?Bing搜索古早BUG至今未改?

首先&#xff0c;大家先看下面的一个数学公式。 Γ ( z ) ∫ 0 ∞ t z − 1 e − t d t . \Gamma(z) \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)∫0∞​tz−1e−tdt. 看不懂&#xff1f;没关系&#xff0c;因为我也看不懂 这不是谈论的重点。 当你把鼠标光标移到公式的最开头&…

小程序-生命周期与WXS脚本

生命周期 什么是生命周期 生命周期&#xff08;Life Cycle&#xff09;是指一个对象从创建 -> 运行 -> 销毁的整个阶段&#xff0c;强调的是一个时间段。 我们可以把每个小程序运行的过程&#xff0c;也概括为生命周期&#xff1a; 小程序的启动&#xff0c;表示生命…

Github 2024-09-23 开源项目周报 Top15

根据Github Trendings的统计,本周(2024-09-23统计)共有15个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目6C++项目3C项目3HTML项目2PowerShell项目1TypeScript项目1JavaScript项目1Blade项目1PHP项目1Bootstrap 5: Web上开发响应式、移动优…

Kafka 面试题

参考&#xff1a; https://javabetter.cn/interview/kafka-40.htmlhttps://javaguide.cn/high-performance/message-queue/kafka-questions-01.html Kafka 架构 名词概念 Producer&#xff08;生产者&#xff09; : 产生消息的一方。 Consumer&#xff08;消费者&#xff09; …

影刀---实现我的第一个抓取数据的机器人

你们要的csdn自动回复机器人在这里文末哦&#xff01; 这个上传的资源要vip下载&#xff0c;如果想了解影刀这个软件的话可以私聊我&#xff0c;我发你 目录 1.网页对象2.网页元素3.相似元素组4.元素操作设置下拉框复选框滚动条获取元素的信息 5.变量6.数据的表达字符串变量列…

数据库主备副本物理复制和逻辑复制对比

数据库主从节点的数据一致性是保证数据库高可用的基本要求&#xff0c;各个数据库在实现方式上也各有异同。而主备复制的方式无外乎两种&#xff1a;物理复制和逻辑复制&#xff0c;本文简要对比下两种方式的不同&#xff0c;并分析下国产数据库是如何实现的。 1、数据库复制基…

初试Bootstrap前端框架

文章目录 一、Bootstrap概述二、Bootstrap实例1、创建网页2、编写代码3、代码说明4、浏览网页&#xff0c;查看结果5、登录按钮事件处理6、浏览网页&#xff0c;查看结果 三、实战小结 一、Bootstrap概述 大家好&#xff0c;今天我们将一起学习一个非常流行的前端框架——Boot…

Redis --- redis事务和分布式事务锁

redis事务基本实现 Redis 可以通过 MULTI&#xff0c;EXEC&#xff0c;DISCARD 和 WATCH 等命令来实现事务(transaction)功能。 > MULTI OK > SET USER "Guide哥" QUEUED > GET USER QUEUED > EXEC 1) OK 2) "Guide哥"使用 MULTI命令后可以输入…

Java数据库连接jdbc

Java数据库连接jdbc 导入java包 1、根目录&#xff0c;新建一个lib目录&#xff08;Dire&#xff09; 2、将jar包放入lib目录下 3、File -> Project Structure&#xff08;项目结构&#xff09; 4、Libraries-> ->java->找到项目的lib目录 5、Apply->OK使用JD…

navicat无法连接远程mysql数据库1130报错的解决方法

出现报错&#xff1a;1130 - Host ipaddress is not allowed to connect to this MySQL serve navicat&#xff0c;当前ip不允许连接到这个MySQL服务 解决当前ip无法连接远程mysql的方法 1. 查看mysql端口&#xff0c;并在服务器安全组中放开相应入方向端口后重启服务器 sud…

二叉树的基本概念(下)

文章目录 &#x1f34a;自我介绍&#x1f34a;二叉树的分类满二叉树完全二叉树 &#x1f34a;二叉树的存储顺序存储[完全二叉树]链式存储 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&#xff08;一键四连&#xff09;哦~ &#x1f34…

顶点缓存对象(VBO)与顶点数组对象(VAO)

我们的顶点数组在CPU端的内存里是以数组的形式存在,想要GPU去绘制三角形,那么需要将这些数据传输给GPU。那这些数据在显存端是怎么存储的呢?VBO上场了,它代表GPU上的一段存储空间对象,表现为一个unsigned int类型的变量,GPU端内存对象的一个ID编号、地址、大小。一个VBO对…

利用探空站数据(怀俄明和IGRA)和ERA5计算ZTD、ZHD和ZWD

1.有关 Matlab 获取代码关注咸鱼获取&#xff1a; 怀俄明探空站数据解算PWV和Tm 怀俄明多线程下载探空站数据&#xff08;包括检查和下载遗漏数据的代码&#xff09; 对IGRAv2进行质量控制得到PWV和Tm的 matlab 代码 算 IGRAv2 探空站的 Tm 和 PWV 提取探空站 IGRAv2 全部数…