Spring中的BeanUtils.copyProperties

BeanUtils.copyProperties() 是 Spring 框架中的一个工具方法,用于将一个 JavaBean 对象的属性值复制到另一个 JavaBean 对象中。其作用是将源对象的属性值复制到目标对象中,从而实现对象属性的拷贝。下面详细解释其作用和原理:

作用:

  1. 属性拷贝:将一个对象的属性值复制到另一个对象中,通常用于 DTO(数据传输对象)和领域模型对象之间的属性拷贝,以便在它们之间进行数据传输和转换。
  2. 简化代码:避免了手动逐个设置属性值的繁琐过程,提高了代码的简洁性和可读性。
  3. 类型转换:在属性值拷贝过程中,BeanUtils 可以自动进行类型转换,使得源对象和目标对象的属性类型不必完全一致。

原理:

  1. 反射:BeanUtils.copyProperties() 方法通过 Java 的反射机制来实现属性值的复制。它会获取源对象和目标对象的所有属性,并逐一进行复制。
  2. 属性名匹配:方法会通过反射机制获取源对象和目标对象的所有属性,并且会根据属性名进行匹配。只有在源对象和目标对象中具有相同名称且可访问的属性时,才会进行属性复制。
  3. 属性赋值:对于匹配的属性,会将源对象中的属性值直接赋值给目标对象的对应属性。这里的赋值操作是直接的引用赋值,不会进行类型转换或者复制属性值所引用的对象。
  4. 类型兼容性:如果源对象的属性类型与目标对象的属性类型不兼容,例如源对象的属性是基本类型,而目标对象的属性是包装类型,或者两者的属性类型不同,那么在赋值过程中会抛出类型转换异常。
  5. 属性过滤:可以通过设置参数来指定要复制的属性列表,从而实现部分属性的复制。

因此,在使用 BeanUtils.copyProperties() 方法时,需要注意确保源对象和目标对象的属性类型和名称都匹配,以避免类型转换异常或属性复制不完整的情况发生。对于类型不匹配的属性,需要提前进行类型转换或者采取其他适当的处理方式。

是浅拷贝还是深拷贝?

BeanUtils.copyProperties() 方法实现的是浅拷贝。

在浅拷贝中,只复制对象的引用,而不复制引用指向的对象。这意味着,如果源对象和目标对象的属性是对象类型(非基本数据类型),则它们在内存中引用的是同一个对象,而不是两个相互独立的对象。因此,如果修改了其中一个对象的属性,另一个对象的相应属性也会受到影响。

举例来说,假设源对象 source 和目标对象 target 都有一个属性 list,它们都引用同一个 ArrayList 对象。当使用 BeanUtils.copyProperties() 方法进行属性复制时,只会复制 list 属性的引用,而不会复制 ArrayList 对象本身。因此,如果修改了 source 对象的 list 属性,target 对象的 list 属性也会发生变化,因为它们引用的是同一个 ArrayList 对象。

如果需要进行深拷贝,即复制对象及其所有属性的副本,可以考虑使用其他工具或手动实现深拷贝逻辑,例如使用第三方库(如 Apache Commons BeanUtils 中的 BeanUtils.cloneBean() 方法)或手动递归复制对象的所有属性。

踩坑记录

1.属性名称和类型匹配问题

源对象和目标对象的属性名称要完全匹配,否则会导致部分属性无法复制或者属性值错误。另外,属性的类型也要保持一致,否则可能会发生类型转换异常。

2.深拷贝问题

BeanUtils.copyProperties() 方法进行的是浅拷贝,如果源对象的属性值是对象类型,并且被多个对象引用,那么目标对象的相应属性值也会引用同一个对象。这可能导致在修改一个对象的属性值时,其他对象的属性值也会受到影响。


java

代码解读

复制代码

//源对象 @Data public class SourceObject { private String name; private int age; private Map<String,String> map; } //目标对象 @Data public class TargetObject { private String name; private int age; private Map<String,String> map; }

image.png

这时目标对象确实赋值了。但是如果改变map对象,目标对象也会跟着改变。

image.png

3.null 值处理

默认情况下,BeanUtils.copyProperties() 方法会将源对象中的所有非 null 属性值复制到目标对象中。如果希望忽略源对象中的 null 属性值,可以使用 BeanUtils.copyProperties() 方法的重载版本,并设置 ignoreNullSource 参数为 true

4.bean对应的属性,没有getter和setter方法

BeanUtils.copyProperties要拷贝属性值成功,需要对应的bean要有getter和setter方法。因为它是用反射拿到set和get方法再去拿属性值和设置属性值的。

5.属性是泛型,不会赋值

推荐使用hutool工具包的BeanUtil.copyProperties()


xml

代码解读

复制代码

<!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.17</version> </dependency>

注意以下几点:

  1. 属性名和数据类型必须匹配

    • 在源对象和目标对象之间进行属性拷贝时,属性名和数据类型必须保持一致。如果属性名或数据类型不匹配,可能会导致拷贝失败或异常。
    • 只有在目标对象已经存在对应的属性时,才会进行属性值的复制。
  2. 自动属性映射

    • 如果源对象和目标对象的属性名不一致,但是数据类型匹配,BeanUtil.copyProperties() 方法会自动进行属性映射,将源对象的属性值复制到目标对象对应的属性中。

    • 1.属性名匹配: 如果源对象和目标对象的属性名完全一致,则直接进行属性值的复制。

    • 2.驼峰命名转换:如果源对象和目标对象的属性名不完全一致,但是符合驼峰命名规范(例如:sourceName 和 targetName),则会尝试进行驼峰命名的转换。例如,将 sourceName 转换为 source_name,然后与目标对象的属性进行匹配。

    • 3.忽略大小写:属性映射时会忽略属性名的大小写。即使源对象和目标对象的属性名在大小写上有差异,也会尝试进行匹配。

  3. 支持嵌套对象的属性复制

    • BeanUtil.copyProperties() 方法支持嵌套对象的属性复制,即如果源对象和目标对象的属性包含其他对象类型的属性,也会递归地进行属性复制。
  4. 支持集合属性的复制

    • Hutool 的 BeanUtil.copyProperties() 方法支持集合属性的复制,即如果源对象和目标对象的属性包含集合类型(如 List、Set、Map 等)的属性,也会进行正确的复制。
  5. 处理 null 值

    • 默认情况下,如果源对象的属性值为 null,BeanUtil.copyProperties() 方法不会将该属性复制到目标对象中,目标对象的属性值保持不变。这种行为可以避免将 null 值赋给目标对象的属性。
  6. 属性是泛型,不会赋值

在Java开发中,特别是在Spring框架中,`BeanUtils.copyProperties` 方法被广泛用于复制对象之间的属性值。该方法通过反射机制实现属性的自动拷贝,从而减少手动编写 `getter` 和 `setter` 的冗余代码。 ### 使用示例 以下是一个简单的使用示例: ```java import org.springframework.beans.BeanUtils; public class Example { public static void main(String[] args) { SourceObject source = new SourceObject(); source.setName("Test"); source.setAge(25); TargetObject target = new TargetObject(); BeanUtils.copyProperties(target, source); // 注意参数顺序:目标在前 System.out.println(target.getName()); // 输出: Test System.out.println(target.getAge()); // 输出: 25 } } class SourceObject { private String name; private int age; // getters and setters public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class TargetObject { private String name; private int age; // getters and setters public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } ``` ### 注意事项 - **属性名称和类型必须一致** `BeanUtils.copyProperties` 在进行属性拷贝时要求源对象和目标对象的属性名完全一致(包括大小写),并且属性类型也必须匹配。如果名称或类型不一致,则不会执行拷贝操作[^1]。 - **泛型仅在编译期有效** 泛型信息在运行时会被擦除,因此不能依赖泛型来做运行期的类型限制。例如,`ArrayList<Integer>` 在运行时实际上是一个普通的 `ArrayList`,可以通过反射添加非整数类型的元素[^4]。 - **可选参数控制拷贝行为** Spring 提供了更灵活的 `copyProperties` 方法签名,允许传入可选的类类型和忽略的属性名列表,以控制哪些属性应该被跳过: ```java private static void copyProperties(Object source, Object target, @Nullable Class<?> editable, @Nullable String... ignoreProperties) ``` 此方法允许开发者指定特定类的属性进行拷贝,并忽略某些不需要复制的字段[^3]。 - **性能考量** 虽然 `BeanUtils.copyProperties` 提供了便利性,但由于其底层依赖于反射机制,因此在频繁调用时可能会影响性能。对于高性能敏感场景,建议使用其他方式如手动赋值或使用 MapStruct 等工具进行优化。 - **异常处理** 该方法可能会抛出 `BeansException` 异常,因此在使用过程中需要做好异常捕获和处理工作。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值