Bean copy 的方式
Apache 的 BeanUtils
位于org.apache.commons.beanutils
包下。通过上面提到的内省机制调用Setter方法实现
。默认实现浅拷贝
,想要实现深拷贝,则需要提供自定义的 Converter
public class BeanUtilsDemo {
public static void main(String[] args) {
long start = System.currentTimeMillis();
UserDO userDO = DataUtil.createData();
log.info("拷贝前,userDO:{}", userDO);
UserDTO userDTO = new UserDTO();
BeanUtils.copyProperties(userDO,userDTO);
log.info("拷贝后,userDO:{}", userDO);
}
}
18:12:11.734 [main] INFO cn.van.parameter.bean.copy.demo.BeanUtilsDemo - 拷贝前,userDO:UserDO(id=1, userName=Van, gmtBroth=2019-11-02T18:12:11.730, balance=100)
18:12:11.917 [main] INFO cn.van.parameter.bean.copy.demo.BeanUtilsDemo - 拷贝后,userDO:UserDO(id=1, userName=Van, gmtBroth=2019-11-02T18:12:11.730, balance=100)
稳定性与效率都不行,不推荐使用。
Apache 的 PropertyUtils
位于org.apache.commons.beanutils
包下。
PropertyUtils
的copyProperties()
方法几乎与BeanUtils.copyProperties()
相同
主要的区别在于BeanUtils.copyProperties()
提供类型转换功能
,即发现两个JavaBean
的同名属性为不同类型
时,在支持的数据类型范围内进行转换
PropertyUtils
不支持
这个功能,所以说BeanUtils使用更普遍一点,犯错的风险更低一点,默认实现浅拷贝。
Apache 的 SerializationUtils
位于org.apache.commons.lang3
包下。
SerializationUtils.clone(T)
,T对象
需要实现 Serializable 接口
,基于JDK序列化
实现了深克隆
深拷贝可以使用。
Spring 的 BeanUtils 使用普遍
位于org.springframework.beans
包下。
其copyProperties
方法实现原理
和Apache BeanUtils.copyProperties原理类似
,默认实现浅拷贝
区别在于对PropertyDescriptor(内省机制相关)的处理结果
做了缓存来提升性能
Spring 的 BeanCopier 推荐使用
位于org.springframework.cglib.beans
包下,默认实现浅拷贝
BeanCopier
支持两种方式
不使用 Converter 的方式,仅对两个 bean 间属性名和类型完全相同的变量进行拷贝
引入Converter,可以对某些特定属性值进行特殊操作,但性能会大大降低
通过操作字节码生成类,来实现原本需要通过反射或者一堆代码才能实现的逻辑,内部还是通过直接调用Setter方法来实现。
使用了 字节码 操作 取代反射 ,使用 缓存 以及通过 Setter 方法设置 属性值 使其 性能非常优秀,推荐使用
实际使用时,可以把创建过的 BeanCopier 实例放到 缓存中,下次可以直接获取,提升性能。
MapStruct 注解实现
默认实现浅拷贝
,通过注解编译时
自动生成拷贝的代码
,基于直接调用Setter方法
实现。
性能还不错,主要是使用方便,加上注解就可以用了。
简介
这里说的是spring
的BeanUtils.copyProperties
BeanUtils
提供对 Java 反射
和自省 API 的包装,其主要目的是利用反射机制对 JavaBean 的属性进行处理
public static void copyProperties(Object source, Object target) throws BeansException {
copyProperties(source, target, (Class)null, (String[])null);
}
内部都是调用下面的私有方法
public static void copyProperties(Object source, Object target, String... ignoreProperties)
ignoreProperties可以是一组需要忽略复制的字符串
public static void copyProperties(Object source, Object target, Class<?> editable)
editable确定需要操作的目前对象类
场景
开发中经常遇到,把父类的属性拷贝到子类中。通常有2种方法:
1、一个一个set
2、用BeanUtils.copyProperties
逐个set
Person p = new Person();
p.setName("Ensk");
p.setAge(18);
p.setGender(1);
p.setMajor("Literature");
Teacher t = new Teacher();
t.setName(p.getName());
t.setAge(p.getAge());
t.setGender(p.getGender());
t.setMajor(p.getMajor());
使用 BeanUtils.copyProperties
Person p = new Person();
p.setName("Ensk");
p.setAge(18);
p.setGender(1);
p.setMajor("Literature");
Teacher t = new Teacher();
BeanUtils.copyProperties(p,t);
很显然BeanUtils更加方便,也美观很多。
复制一个对象并且不想复制其中的空值属性
Java复制一个对象并且不想复制其中的空值属性
BeanUtils.copyProperties参数为null的时候不复制
示例
示例一
List集合之间的对象属性赋值
/**
* @param input 输入集合
* @param clzz 输出集合类型
* @param <E> 输入集合类型
* @param <T> 输出集合类型
* @return 返回集合
*/
public static <E, T> List<T> convertList2List(List<E> input, Class<T> clzz) {
List<T> output = Lists.newArrayList();
if (CollectionUtils.isNotEmpty(input)) {
for (E source : input) {
T target = BeanUtils.instantiate(clzz);
BeanUtil.copyProperties(source, target);
output.add(target);
}
}
return output;
}
两个类User
和Employee
,将存储Employee对象的List赋给存储User对象的List
public class User {
private String name;
private Integer age;
...
}
public class Employee {
private String name;
private Integer age;
private String dept;
...
}
@RunWith(PowerMockRunner.class)
public class TestUtil
{
@Test
public void test(){
Employee ee1=new Employee("A",21,"it");
Employee ee2=new Employee("B",23,"account");
User user=new User();
BeanUtil.copyProperties(ee1, user);
System.out.println(user);
System.out.println("-------------分割线--------------");
List<User> output=new ArrayList<>();
List<Employee> source= Arrays.asList(ee1,ee2);
output=BeanUtil.convertList2List(source,User.class);
for (User str:output) {
System.out.println(str);
}
}
}
示例二
1. 创建一个实体类
//lombok中的注解,省去添加settter和getter方法
@Data
public class Book {
private String username;
private String password;
private String email;
}
2. 创建被复制的实体类
//lombok中的注解,省去添加settter和getter方法
@Data
public class Book {
public class Book2 {
private String username;
private String password;
private String email;
}
3. 测试
public static void main(String[] args) {
Book book = new Book();
book.setEmail("abc@163.com");
book.setPassword("123456");
book.setUsername("happygiraffe");
Book2 book2 = new Book2();
//添加了忽略username属性的赋值
BeanUtils.copyProperties(book,book2,"username");
System.out.println(book.toString());
System.out.println(book2.toString());
}
4. 打印结果:
Book{username='happygiraffe', password='123456', email='abc@163.com'}
Book2{username='null', password='123456', email='abc@163.com'}
问题
注意
spring
和apache
的copy属性的方法源和目的参数
的位置正好相反
,所以导包和调用的时候都要注意一下。
从 src 拷贝到 dest 中
spring 的 BeanUtils.copyProperty(src,dest)
commons 的 BeanUtils.copyProperty(dest,src)
BeanUtils.copyProperties(a, b);
b 中的存在的属性,a 中一定要有,但是 a 中 可以有多余的属性
a 中与 b 中 相同的属性都会被替换 ,不管是否有值
a、 b中的属性要 名字相同 ,才能被赋值,不然的话需要手动赋值
存在名称不相同的属性,则 BeanUtils 不对这些属性进行处理,需要手动处理
Spring 的 BeanUtils.CopyProperties方法 需要对应的属性 有getter和setter方法
如果 存在属性完全相同的内部类,但是不是同一个内部类,即分别属于各自的内部类,则spring会认为属性不同,不会copy;
BeanUtils是深拷贝,还是浅拷贝?
是浅拷贝
浅拷贝: 只是调用子对象的set方法,并没有将所有属性拷贝。(也就是说,引用的一个内存地址)
深拷贝: 将子对象的属性也拷贝过去。
什么情况适合用BeanUtils
如果都是单一的属性
,那么不涉及到深拷贝的问题
,适合用BeanUtils。
有子对象就一定不能用BeanUtils么
并不绝对,这个要区分考虑:
1、子对象还要改动。
2、子对象不怎么改动。
虽然有子对象
,但是子对象并不怎么改动
,那么用BeanUtils也是没问题的。