BeanUtils 简述

在 Spring 项目里,开发者经常会在 "把数据从一个对象拷贝到另一个对象" 的场景下碰到 BeanUtils 这个工具类。Spring 生态里其实有两套名字相同但实现不同的 BeanUtils

  1. Spring 自带的 org.springframework.beans.BeanUtils
  2. Apache Commons BeanUtils (org.apache.commons.beanutils.BeanUtils)

日常 Spring 项目里最常见的是 Spring 自带的版本,以下也是介绍此版本。

一、Spring 的 BeanUtils 能做什么?

常见方法 作用 备注
copyProperties(Object source, Object target) 把源对象的同名同类型属性拷贝到目标对象 最常用的方法
copyProperties(Object source, Object target, Class<?> editable) 同上,但只拷贝 editable 类里声明的属性 可做白名单
copyProperties(Object source, Object target, String... ignoreProperties) 同上,但忽略指定属性 可做黑名单
instantiateClass(Class<T> clazz) 用无参构造器实例化类 内部帮你处理了异常
findMethod(...)/getPropertyDescriptor(...) 反射工具 框架内部用得多

使用时的代码示例:

java 复制代码
import org.springframework.beans.BeanUtils;

@Data
class Source {
    private Long id;
    private String name;
    private List<String> tags;
}

@Data
class Target {
    private Long id;
    private String name;
    private List<String> tags;
}

public class Demo1 {
    public static void main(String[] args) {
        Source src = new Source();
        src.setId(1L);
        src.setName("Tom");
        src.setTags(Arrays.asList("java", "spring"));

        Target tar = new Target();
        
        // 方法一
        BeanUtils.copyProperties(src, tar);   // ← 关键一行
        System.out.println(tar);
        // Target(id=1, name=Tom, tags=[java, spring])
        
        // 方法二
        BeanUtils.copyProperties(src, tar, "id", "tags"); // id、tags 不会被拷贝
        
        // 方法三(省略了父类 / 接口声明的白名单)
        BeanUtils.copyProperties(src, tar, Base.class); // 只拷贝 Base 中声明的字段
    }
}

二、常见陷阱和建议

1、小心浅拷贝

BeanUtils.copyProperties 只复制了字段值,集合 / 数组里的元素仍然是同一引用

java 复制代码
List<Order> orders = new ArrayList<>();
source.setOrders(orders);
BeanUtils.copyProperties(source, target); // target.orders 与 source.orders 指向同一对象

2、不会触发 setter / getter 里的业务逻辑

BeanUtils.copyProperties 基于反射 + 缓存,性能较快,但也因为直接用反射写字段,任何 setter 里的校验、计算都会被跳过。

3、字段名 / 类型必须完全一致

一个字母的大小写不同或类型不兼容就拷不过去,也不会报错,只是值为 null。

三、什么是反射?

反射(Reflection)是 Java 在运行时动态加载类、探查类结构(字段、方法、构造器),并对其进行实例化、调用或赋值的机制。

你可以把"反射"想象成 Java 语言的"后门":正常情况下,你只能通过 new 或调用公开方法去使用一个类;有了反射,你就可以在运行时"拆开"类、对象、方法、字段,随意查看甚至修改,哪怕它们是 private 的。

java 复制代码
// 正常写法
Dog dog = new Dog();
dog.setName("旺财");

// 反射写法(等价功能)
Class<?> clazz = Class.forName("com.xxx.Dog");             // 1. 运行时把类加载进来
Object dog = clazz.getDeclaredConstructor().newInstance(); // 2. 创建对象
Method setName = clazz.getMethod("setName", String.class); // 3. 拿方法
setName.invoke(dog, "旺财");                               // 4. 调用方法

总结,反射就是运行时"透视"并操控 Java 类的超能力,框架靠它实现"零配置"和"动态扩展",代价是性能略慢、破坏封装,需要谨慎使用。

四、考虑包含 null 的情形

如果 srouce 或 target 本身为 null,Spring 的 BeanUtils.copyProperties 会立即抛出 IllegalArgumentException,拷贝动作根本不会发生。

java 复制代码
// 源码
public static void copyProperties(Object source, Object target, @Nullable Class<?> editable, @Nullable String... ignoreProperties) {
    Assert.notNull(source, "Source must not be null");
    Assert.notNull(target, "Target must not be null");
    ...
}

如果源对象的字段本身就是 null,那么目标字段一定会被覆写成 null(或基本类型的默认值),不会保留原值,也不会报错。

相关推荐
程序猿DD15 分钟前
Spring Boot 4 与 Spring Framework 7 全面解析:新特性、升级要点与实战指南
java·spring boot·后端
用户61204149221325 分钟前
jsp+servlet做的咖啡品牌管理后台
java·前端·后端
汇匠源33 分钟前
基于springboot家政、上门服务、Java源码系统功能结构
java·spring boot·后端
tuokuac38 分钟前
异常:java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter
java
麦兜*1 小时前
Spring Boot集群 集成Nginx配置:负载均衡+静态资源分离实战
java·spring boot·后端·nginx·spring·缓存·负载均衡
一叶飘零_sweeeet1 小时前
攻克 大 Excel 上传难题:从异步处理到并发去重的全链路解决方案
java·excel·大文件上传
huan19931 小时前
Java中实现html转pdf
java
野犬寒鸦1 小时前
从零起步学习Redis || 第二章:Redis中数据类型的深层剖析讲解(下)
java·redis·后端·算法·哈希算法
王者鳜錸1 小时前
方言普通话识别大模型,支持中英+202种方言识别
java·vue·语音识别