【问题标题】:Copy properties from one bean to another (not the same class) recursively (including nested beans) [duplicate]递归地将属性从一个bean复制到另一个(不是同一个类)(包括嵌套bean)[重复]
【发布时间】:2015-05-31 14:11:35
【问题描述】:

哪种方法需要最少的自己编写的代码来实现一个 bean 到另一个 bean 的深拷贝?目标是在源属性和目标属性按名称匹配时以自动方式执行此操作。

源主 bean:

public class SourceBean {
    private String beanField;
    private SourceNestedBean nestedBean;

    // getters and setters
}

源嵌套 bean:

public class SourceNestedBean {
    private String nestedBeanField;

    // getters and setters
}

目标主 bean:

public class TargetBean {
    private String beanField;
    private TargetNestedBean nestedBean;

    // getters and setters        
}

目标嵌套 bean:

public class TargetNestedBean {
    private String nestedBeanField;

    // getters and setters
}


使用例如Spring BeanUtils.copyProperites() 我可以用一行代码创建一个SourceBeanTargetBean 的浅拷贝,但它不会复制嵌套的bean。是否有任何成熟的实用程序(不一定是 Spring Framework)可以在尽可能少地编写自己的代码(与 BeanUtils.copyProperties() 几乎相同)的同时进行深度复制?

【问题讨论】:

  • 使用序列化深拷贝
  • @TheLostMind 我认为这只有在他们是同一个班级时才有效。
  • @M.Deinum 不完全是。您提到的问题是一个更通用的问题(bean 映射),而我需要的是用尽可能少的设置代码以非常简单的方式克隆 by filed name
  • @TheLostMind 该副本是从SourceBean 类的对象到TargetBean 类的对象,它们没有共同的祖先。
  • @RealSkeptic 你是对的,序列化不是一个选项,因为我们在谈论两个不同的类

标签: java spring javabeans spring-bean


【解决方案1】:

一种方法是使用Jackson ObjectMapper,通过convertValue() method

ObjectMapper mapper = new ObjectMapper();
SourceBean source = ...;
TargetBean target = mapper.convertValue(source, TargetBean.class);

请注意,convertValue() 已被重载以也适用于泛型类型。另请注意,convertValue() 在某些情况下会返回您提供的值,例如 SourceBean 是否可分配给 TargetBean。

由于 Jackson 是一个 JSON 序列化/反序列化库,convertValue() 所做的是将source 序列化为内存中的 JSON,然后将此 JSON 反序列化为 TargetBean 的实例。所以高性能是不可预期的。但是,转换是用一行代码执行的。

如果您需要性能,最好手动进行映射。没有比这更好的了。

如果您需要简单性,请按照上述说明使用 Jackson。

一个不错的折衷方案是Orika,这是一个几乎没有配置且不使用反射的高性能映射器。

【讨论】:

  • 我想这可以被接受为答案,尽管由于可能的性能问题,我仍然不确定我是否会在生产中使用它。
  • @SergeyPauk 我唯一能回应的是你应该运行一些基准测试,看看时间对你来说是否合理。
  • 这是一个糟糕的答案,转换选项不会创建不可变 bean。如果 bean 中包含非原始类型,则更改目标 bean 将更改源 bean。
  • 是的,我做到了。它改变了两个 bean 中的值
  • 在我的情况下它不起作用。您的应用程序工作正常,但我的没有.. 使用相同的杰克逊版本...经过代码检查,我能够发现原因。就我而言,我正在复制相同的 bean 类型: SourceBean target = mapper.convertValue(source, SourceBean.class);但是在您的情况下,您正在克隆到另一种 bean 类型。克隆到相同类型(深 bean 副本)不是一成不变的。所以你的答案适用于问题案例,但不适用于其他案例(这是我的)
【解决方案2】:

您可以使用 Dozer Mapper 进行深度复制。见http://dozer.sourceforge.net/documentation/deepmapping.html

【讨论】:

  • 是否可以使用 Dozer w/o xml 配置,是否可以定义按名称映射策略,因为我不想手动定义任何特定的映射?
  • 您可以改用 API 映射:dozer.sourceforge.net/documentation/apimappings.html。我认为编写通用的辅助方法来复制您的对象并不难。
  • 是的,这个不使用 XML,但我仍然必须定义要复制的字段,所以假设我有几十个 bean,不幸的是这不是一个选项。
  • 无论如何感谢您的帮助,点赞
【解决方案3】:

如果你想在 Java 中使用深拷贝,你应该使用 ObjectOutputStream 和 ObjectInputStream 并且你需要复制的所有类都应该实现 Serializable。

public Object deepCopy() throws IOException, ClassNotFoundException{
    //store object in memory using serial
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(this);
    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bis);
    return ois.readObject();
}

【讨论】:

  • 源ant目标对象属于不同的类
【解决方案4】:

使用来自 apache commons-lang 的 SerializationUtils。并使您的对象可序列化。

【讨论】:

  • 源ant目标对象属于不同的类
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多