Spring的BeanUtils.copyProperties属性复制避坑:
项目场景:在分布式项目中,我进行我们的项目对外接口整合时,需要自己再定义对外的类来传参,就避免不了用BeanUtils.copyProperties工具来进行属性赋值。
问题描述
关于BeanUtils.copyProperties的属性复制规则
话不多说直接上demo代码
类A
class A {
int a;
List<Integer> list = new ArrayList<>();
List<B> bList = new ArrayList<>();
B b;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
public List<Integer> getList() {
return list;
}
public void setList(List<Integer> list) {
this.list = list;
}
public List<B> getbList() {
return bList;
}
public void setbList(List<B> bList) {
this.bList = bList;
}
@Override
public String toString() {
return "A{" +
"a=" + a +
", list=" + list +
", bList=" + bList +
", b=" + b +
'}';
}
}
类A1
class A1 {
int a;
List<Integer> list = new ArrayList<>();
List<B> bList = new ArrayList<>();
B b;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
public List<Integer> getList() {
return list;
}
public void setList(List<Integer> list) {
this.list = list;
}
public List<B> getbList() {
return bList;
}
public void setbList(List<B> bList) {
this.bList = bList;
}
@Override
public String toString() {
return "A1{" +
"a=" + a +
", list=" + list +
", bList=" + bList +
", b=" + b +
'}';
}
}
类B
class B {
String b;
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
@Override
public String toString() {
return "B{" +
"b='" + b + '\'' +
'}';
}
public B() {
}
public B(String b) {
this.b = b;
}
}
测试main方法1
public static void main(String[] args) {
A a = new A();
B b = new B();
b.setB("bbb");
List<Integer> list = new ArrayList<>();
list.add(100);
list.add(200);
List<B> bList = new ArrayList<>();
bList.add(new B("dd"));
bList.add(new B("cc"));
bList.add(new B("ee"));
a.setA(1);
a.setB(b);
a.setList(list);
a.setbList(bList);
A1 a1 = new A1();
BeanUtils.copyProperties(a, a1);
System.out.println(a1);
}
结果::A1{a=1, list=[100, 200], bList=[B{b=‘dd’}, B{b=‘cc’}, B{b=‘ee’}], b=B{b=‘bbb’}}
测试main方法2
public static void main(String[] args) {
A a = new A();
B b = new B();
b.setB("bbb");
List<Integer> list = new ArrayList<>();
list.add(100);
list.add(200);
List<B> bList = new ArrayList<>();
bList.add(new B("dd"));
bList.add(new B("cc"));
bList.add(new B("ee"));
a.setA(1);
a.setB(b);
a.setList(list);
a.setbList(bList);
A1 a1 = new A1();
BeanUtils.copyProperties(a, a1);
b.setB("aaa");
a.setA(2);
list.clear();
list.add(666);
list.add(777);
bList.clear();
bList.add(new B("abc"));
System.out.println(a1);
}
结果:A1{a=1, list=[666, 777], bList=[B{b=‘abc’}], b=B{b=‘aaa’}}
结果分析
从以上的例子和结果来看,BeanUtils.copyProperties是浅拷贝。
main方法1
: 当A和A1中的属性名称相同时,使用BeanUtils.copyProperties会将所有属性进行复制。从结果上来看,不管是基本数据类型int a
,还是剩余的三个引用数据类型,List<Integer> list
、List<B> bList
、B b
,都进行了复制。
main方法2
:第二个main方法在第一个的基础上对要复制的A的属性进行了修改,从结果上发现,A1的属性中基本数据类型int a
的值并没有因为修改而改变,但引用数据类型List<Integer> list
、List<B> bList
、B b
的值会因为改变了A的属性而改变。
附带两种对象属性复制的方法
// 浅拷贝
a1 = JSONObject.parseObject(JSONObject.toJSONString(a), A1.class)
// 深拷贝,其中SerializerFeature.DisableCircularReferenceDetect是为了解决JSON的$ref 引用
a1 = JSONObject.parseObject(JSONObject.toJSONString(a, SerializerFeature.DisableCircularReferenceDetect), A1.class);
// 深拷贝
ObjectMapper objectMapper = new ObjectMapper();
a1 = objectMapper.readValue(JSONObject.toJSONString(a, SerializerFeature.DisableCircularReferenceDetect), A1.class);
结论
BeanUtils.copyProperties的拷贝是浅拷贝,对基本数据类型进行值传递的属性复制,改变原基本数据类型的值不会改变被复制的属性结果;对引用数据类型进行引用传递的属性复制,只是复制了一个内存地址过去,指向的还是同一个对象,并没有新建对象,改变原对象的值,被复制的属性的值也会随着变化。