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(或基本类型的默认值),不会保留原值,也不会报错。

相关推荐
最初的↘那颗心1 分钟前
Flink Stream API 源码走读 - print()
java·大数据·hadoop·flink·实时计算
JH30731 小时前
Maven的三种项目打包方式——pom,jar,war的区别
java·maven·jar
带刺的坐椅2 小时前
轻量级流程编排框架,Solon Flow v3.5.0 发布
java·solon·workflow·flow·solon-flow
David爱编程2 小时前
线程调度策略详解:时间片轮转 vs 优先级机制,面试常考!
java·后端
阿冲Runner3 小时前
创建一个生产可用的线程池
java·后端
写bug写bug3 小时前
你真的会用枚举吗
java·后端·设计模式
喵手4 小时前
如何利用Java的Stream API提高代码的简洁度和效率?
java·后端·java ee
-Xie-4 小时前
Maven(二)
java·开发语言·maven
IT利刃出鞘4 小时前
Java线程的6种状态和JVM状态打印
java·开发语言·jvm
天天摸鱼的java工程师5 小时前
Java 解析 JSON 文件:八年老开发的实战总结(从业务到代码)
java·后端·面试