BeanUtils.copyProperties(target, source)-在两个 Java 对象之间复制属性值


BeanUtils.copyProperties 是 Apache Commons BeanUtils 库中的一个方法,用于在两个 Java 对象之间复制属性值。它通过反射机制动态地获取源对象的属性值,并将其赋值给目标对象的对应属性。

尽管 BeanUtils.copyProperties 非常方便,但它并不支持复杂对象的深度复制(deep copy)。为了更好地理解它的功能和限制,我们需要详细分析其工作原理、使用场景以及如何处理复杂对象。


1. 基本用法

BeanUtils.copyProperties 的基本语法如下:

import org.apache.commons.beanutils.BeanUtils;

public class Main {
    public static void main(String[] args) throws Exception {
        // 源对象
        Person source = new Person();
        source.setName("Alice");
        source.setAge(25);

        // 目标对象
        Person target = new Person();

        // 复制属性
        BeanUtils.copyProperties(target, source);

        // 输出结果
        System.out.println("Target Name: " + target.getName());
        System.out.println("Target Age: " + target.getAge());
    }
}

class Person {
    private String name;
    private int age;

    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;
    }
}

输出:

Target Name: Alice
Target Age: 25

解释:

  • BeanUtils.copyProperties(target, source)source 对象的属性值复制到 target 对象。
  • 它会自动匹配源对象和目标对象中具有相同名称和兼容类型的属性。

2. 工作原理

BeanUtils.copyProperties 的工作流程如下:

  1. 通过反射获取属性 :
  • 使用 Class.getDeclaredFields() 或类似的方法获取源对象的所有属性。
  • 检查目标对象是否具有相同名称的属性。
  1. 调用 getter 和 setter 方法 :
  • 调用源对象的 getter 方法获取属性值。
  • 调用目标对象的 setter 方法设置属性值。
  1. 类型转换 :
  • 如果源对象和目标对象的属性类型不同,BeanUtils 会尝试进行简单的类型转换(如 StringInteger)。

3. 支持简单对象的浅复制

BeanUtils.copyProperties 默认只支持浅复制(shallow copy),即它只会复制对象的字段值,而不会递归复制嵌套对象的字段。

示例:浅复制

class Address {
    private String city;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

class Person {
    private String name;
    private Address address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        // 创建源对象
        Address sourceAddress = new Address();
        sourceAddress.setCity("New York");

        Person source = new Person();
        source.setName("Bob");
        source.setAddress(sourceAddress);

        // 创建目标对象
        Person target = new Person();

        // 复制属性
        BeanUtils.copyProperties(target, source);

        // 修改源对象的地址
        source.getAddress().setCity("Los Angeles");

        // 输出结果
        System.out.println("Source City: " + source.getAddress().getCity());
        System.out.println("Target City: " + target.getAddress().getCity());
    }
}

输出:

Source City: Los Angeles
Target City: Los Angeles

解释:

  • BeanUtils.copyProperties 只复制了 address 引用,而不是创建一个新的 Address 对象。
  • 因此,修改源对象的 address.city 会影响目标对象的 address.city

4. 不支持深度复制的原因

BeanUtils.copyProperties 不支持深度复制的主要原因在于:

  1. 引用传递 :
  • 它直接将源对象的引用赋值给目标对象,导致两者共享同一个嵌套对象。
  1. 性能开销 :
  • 深度复制需要递归遍历所有嵌套对象并创建新实例,这会显著增加性能开销。
  1. 复杂性 :
  • 嵌套对象可能包含循环引用或复杂的结构,处理这些情况需要额外的逻辑。

5. 如何实现深度复制

如果需要深度复制,可以结合以下方法:

方法 1:手动实现深度复制

为每个类提供一个 clone 方法或类似的逻辑,递归复制所有嵌套对象。

class Address implements Cloneable {
    private String city;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    protected Object clone() {
        Address cloned = new Address();
        cloned.setCity(this.city);
        return cloned;
    }
}

class Person implements Cloneable {
    private String name;
    private Address address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    protected Object clone() {
        Person cloned = new Person();
        cloned.setName(this.name);
        cloned.setAddress((Address) this.address.clone());
        return cloned;
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        // 创建源对象
        Address sourceAddress = new Address();
        sourceAddress.setCity("New York");

        Person source = new Person();
        source.setName("Charlie");
        source.setAddress(sourceAddress);

        // 深度复制
        Person target = (Person) source.clone();

        // 修改源对象的地址
        source.getAddress().setCity("San Francisco");

        // 输出结果
        System.out.println("Source City: " + source.getAddress().getCity());
        System.out.println("Target City: " + target.getAddress().getCity());
    }
}

输出:

Source City: San Francisco
Target City: New York

解释:

  • clone 方法递归复制了 Address 对象,确保源对象和目标对象的嵌套对象是独立的。

方法 2:使用第三方库(如 MapStruct 或 Jackson)

一些现代库(如 MapStruct 或 Jackson)支持深度复制,并且更加高效。

示例:使用 Jackson 进行深度复制

import com.fasterxml.jackson.databind.ObjectMapper;

class Address {
    private String city;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

class Person {
    private String name;
    private Address address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();

        // 创建源对象
        Address sourceAddress = new Address();
        sourceAddress.setCity("New York");

        Person source = new Person();
        source.setName("David");
        source.setAddress(sourceAddress);

        // 深度复制
        Person target = objectMapper.readValue(objectMapper.writeValueAsString(source), Person.class);

        // 修改源对象的地址
        source.getAddress().setCity("Chicago");

        // 输出结果
        System.out.println("Source City: " + source.getAddress().getCity());
        System.out.println("Target City: " + target.getAddress().getCity());
    }
}

输出:

Source City: Chicago
Target City: New York

解释:

  • Jackson 将对象序列化为 JSON,然后反序列化为目标对象,从而实现深度复制。

6. 总结

  • 浅复制 :BeanUtils.copyProperties 默认只支持浅复制,适用于简单对象。
  • 深度复制 :需要手动实现(如 clone 方法)或借助工具(如 Jackson)。
  • 适用场景 :
  • 如果对象结构简单且不需要深度复制,可以直接使用 BeanUtils.copyProperties
  • 如果对象包含嵌套结构或需要深度复制,建议使用 Jackson 或其他专用工具。

如果你有更具体的需求或问题,请进一步说明,我可以为你提供更详细的解决方案!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

苍煜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值