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


在这里插入图片描述

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可能是一个值得考虑的选项。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

执键行天涯

码你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值