我hava Foo和Item类如下。
import java.util.ArrayList;
import java.util.List;
public class Foo {
private Long id;
private List<Item> items;
public Foo(Long id) {
this.id = id;
this.items = new ArrayList<Item>();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<Item> getItems() {
return items;
}
public void setItems(List<Item> items) {
this.items = items;
}
}
public class Item {
private String bar;
public Item(String bar) {
this.bar = bar;
}
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
@Override
public String toString() {
return "Item{" + "bar='" + bar + '\'' + '}';
}
}
当我使用spring BeanUtils复制Foo类时,列表字段的引用不会改变。
import org.springframework.beans.BeanUtils;
public class SimpleCopyMain {
public static void main(String[] args) {
Foo foo = new Foo(1L);
foo.getItems().add(new Item("item1"));
foo.getItems().add(new Item("item2"));
Foo fooSnapShot = new Foo(100L);
BeanUtils.copyProperties(foo,fooSnapShot);
foo.setId(999L);
System.out.println("fooSnapShot id field value is not changing as expected : " + fooSnapShot.getId());
foo.getItems().add(new Item("item3"));
System.out.println("fooSnapShot items value is changing unexpectedly : " + fooSnapShot.getItems());
}
}
SimpleCopyMain类的输出如下:
fooSnapShot id field value is not changing as expected : 1
fooSnapShot items value is changing unexpectedly : [Item{bar='item1'}, Item{bar='item2'}, Item{bar='item3'}]
但是,当我为列表字段创建一个新实例并逐个复制引用时,我得到了我预期的行为。
import java.util.ArrayList;
import org.springframework.beans.BeanUtils;
public class CopyMain {
public static void main(String[] args) {
Foo foo = new Foo(1L);
foo.getItems().add(new Item("item1"));
foo.getItems().add(new Item("item2"));
Foo fooSnapShot = new Foo(100L);
BeanUtils.copyProperties(foo, fooSnapShot);
fooSnapShot.setItems(new ArrayList<Item>(foo.getItems().size()));
for(int i = 0; i < foo.getItems().size(); i++){
Item anItem = new Item("");
BeanUtils.copyProperties(foo.getItems().get(i), anItem);
fooSnapShot.getItems().add(anItem);
}
foo.setId(999L);
System.out.println("fooSnapShot id field value is not changing as expected : " + fooSnapShot.getId());
foo.getItems().add(new Item("item3"));
System.out.println("fooSnapShot items value is is not changing : " + fooSnapShot.getItems());
}
}
这是输出:
fooSnapShot id field value is not changing as expected : 1
fooSnapShot items value is is not changing : [Item{bar='item1'}, Item{bar='item2'}]
我的pom:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.question</groupId>
<artifactId>beanutils</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
</dependencies>
</project>
为什么spring beanutils没有克隆列表字段?
答案 0 :(得分:5)
根据BeanUtil copyProperities方法实现,Spring正在通过Getters和Setters复制您的数据。如果你有像Integer这样的原语,那没关系,但对于你的List字段,你在Setter中传递引用。
如果您希望它能够正常工作,您需要将您的二传手更改为:
public void setItems(List<Item> items) {
this.items = new ArrayList<>(items);
}
这也是浅拷贝,但你不会有列表参考。
答案 1 :(得分:3)
如果查看spring的BeanUtils.copyProperties,您可以看到所有正在执行的操作都是执行属性的浅表副本,这意味着只会克隆具有原始值的属性,所有其他属性将通过引用进行复制。在幕后,Spring使用PropertyDescriptor并在source属性上调用getter并在target属性中调用setter。
所以当你在那一刻调用BeanUtils.copyProperties(foo, fooSnapShot);
时,foo和fooSnapShot共享对项目列表的相同引用,这就是为什么可以通过foo或fooSnapshot实例更改列表的原因,但是第二种情况,你给fooSnapShot一个不同的列表fooSnapShot.setItems(new ArrayList<Item>(foo.getItems().size()));
的引用,这就是你获得预期结果的原因。