简介
说明
本文用实例介绍Spring的BeanUtils的拷贝原理。
概述
Spring的BeanUtils是浅拷贝:
- 如果要拷贝的成员是基本数据类型,它会拷贝成员的值,相当于数据是独立的。这里的基本数据类型包括基础类型和包装类型,例如:Integer、int、String、Long、long等)
- 如果要拷贝的成员不是基本数据类型,就拷贝引用地址(使用同一个引用地址(也就是:同一个对象))。
实例
代码
Entity
User
package com.knife.entity; import lombok.Data; @Data public class User { private Long id; private String userName; private UserDetail userDetail; }
UserDetail
package com.knife.entity; import lombok.Data; @Data public class UserDetail { private Long id; private String address; }
Controller
package com.knife.controller; import com.knife.entity.User; import com.knife.entity.UserDetail; import org.springframework.beans.BeanUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/test") public String test() { User user = new User(); user.setId(1L); user.setUserName("Tony"); UserDetail userDetail = new UserDetail(); userDetail.setId(2L); userDetail.setAddress("上海"); user.setUserDetail(userDetail); User user1 = new User(); BeanUtils.copyProperties(user, user1); return "test success"; } }
测试
打断点:
结果:
可以看到,user和user1它们的userDetail成员是同一个对象:{UserDetail@6602}
原理分析
打断点
运行到这个地方:
BeanUtils#copyProperties(java.lang.Object, java.lang.Object, java.lang.Class<?>, java.lang.String…)
如下图所示:
可以看到,targetPds里边包含了目标对象的所有成员,依次对它处理,看它下半部分的代码:
可以发现,它是使用反射,先调用源对象的getXxx方法获取值,再调用目标对象的setXxx方法将值设置进去。
所以,当成员是基本类型之外的对象时,它getXxx会获取到引用地址,setXxx会将引用地址设置进去,所以会共用同一个对象。
深拷贝的方案
方案1:使用JSON
先将源对象转化为json字符串,再将json字符串转为对象。
JSON工具类见:SpringBoot-封装JSON工具类(基于Jackson) – 自学精灵
方案2:使用serializable接口
使用序列化接口,结合内存输入输出流。
请先
!