BeanUtils.copyProperties

36 篇文章 1 订阅

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包下。
PropertyUtilscopyProperties()方法几乎与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方法实现。

性能还不错,主要是使用方便,加上注解就可以用了。

简介

这里说的是springBeanUtils.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;
}

两个类UserEmployee,将存储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'}   

问题

注意

springapache的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 不对这些属性进行处理,需要手动处理

SpringBeanUtils.CopyProperties方法 需要对应的属性 有getter和setter方法

如果 存在属性完全相同的内部类,但是不是同一个内部类,即分别属于各自的内部类,则spring会认为属性不同,不会copy;

BeanUtils是深拷贝,还是浅拷贝?

浅拷贝

浅拷贝: 只是调用子对象的set方法,并没有将所有属性拷贝。(也就是说,引用的一个内存地址)
深拷贝: 将子对象的属性也拷贝过去。

什么情况适合用BeanUtils

如果都是单一的属性,那么不涉及到深拷贝的问题,适合用BeanUtils。

有子对象就一定不能用BeanUtils么

并不绝对,这个要区分考虑:

1、子对象还要改动。
2、子对象不怎么改动。

虽然有子对象,但是子对象并不怎么改动,那么用BeanUtils也是没问题的。

  • 4
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值