BeanUtils.copyProperties的用法(超详细,建议收藏)

2023-10-27

常见场景

我们如果有两个具有很多相同属性名的JavaBean对象a和b,想把a中的属性赋值到b,例如

  • 接口中将接收到的前端请求参数XxxReqVo,我们想把这个入参转化为XxxQuery对象作为数据库的查询条件对象

传统做法是手动set,即

XxxQuery xxxQuery = new XxxQuery();
xxxQuery .setAxx(xxxReqVo.getAxx());
xxxQuery .setBxx(xxxReqVo.getBxx());
xxxQuery .setCxx(xxxReqVo.getCxx());

如果有几十个需要赋值的的字段呢?那就很头疼了org.springframework.beans.BeanUtils,它提供了对java反射和自省API的包装。它里面还有很多工具类,这里我们介绍一下该类下面的copyProperties方法,该工具方法可以帮我们大大简化这一步

应用案例

案例一(两个无关的类做属性拷贝)

@Data
public class User {
    private String id;
    private String name;
    private String age;
    private String account;
    private String password;
}
@Data
public class Person {
    private String id;
    private String name;
    private String age;
    private String sex;
}
public class Test {
    public static void main(String[] args) {
        User user = new User();
        user.setId("1");
        user.setAge("2");
        user.setName("wzh");
        user.setAccount("wangzh");
        user.setPassword("1111");
        Person person = new Person();
        BeanUtils.copyProperties(user,person);
    }
}

结果

Person(id=1, name=wzh, age=2, sex=null)

通过上述测试我们就可以总结出相关结论,基本用法为

BeanUtils.copyProperties(source,target);

相当于把源对象source的属性值赋给目标对象target中与源对象source的中有着同属性名的属性,如上述案例中Person作为目标对象与源对象User中有着共同的同名属性id,name,age,所以person中的这三个字段被赋值成功,赋值的数据来源正是user对象,sex这个字段是Person类所特有的,所以不会被赋值,同时还要特别注意赋值操作相关类的属性一定要有对应的setter/getter,即

  • 源对象source的属性拷贝值赋给目标对象target的过程中,属性名和属性类型都相同的属性才能被成功拷贝赋值,例如id,name,age这三个目标对象的属性被赋值成功,目标对象中的sex属性,由于源对象中没有同名的属性所以没法被赋值成功。
  • 做赋值的属性一定要有对应的setter/getter才能成功赋值

案例二(父子类之间做赋值)

这里难度升级一下,我们不仅仅要演示父子类之间做赋值,还要加一个特征,即在父类Person具有String类型的age属性的基础上,子类中定义一个Integer类型的属性

父类Person

@Data
public class Person {
    private String id;
    private String name;
    private String age;
    private String sex;

    public void test(Object obj) {
        System.out.println("test");
    }

    @Override
    public String toString() {
        return "Person{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}

子类User,类内含有测试用的main方法

import org.springframework.beans.BeanUtils;

public class User extends Person{
    private Integer age;
    private String account;
    private String password;

    public <T> T getAge(Boolean isSon) {
        if(isSon == true) {
            return (T) this.age;
        }

        return (T) super.getAge();
    }

    //重载:方法名相同,但返回值数目/类型/不同类型返回值的顺序非一致,
    // setAge方法满足重载条件,getAge则无法满足重载条件
    public void setAge(String age) {
         super.setAge(age);
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public static void main(String[] args) {
        User user = new User();
        user.setId("1");
        // 设置父类的age属性值
        user.setAge("23");
        // 设置子类的age属性值
        user.setAge(2);
        user.setName("wzh");
        user.setAccount("wangzihan06");
        user.setPassword("123456");

        // 获取子类的age属性,子类的age属性是Interger类型,需要用Interger接收
        Integer age1 = user.getAge(true);
        System.out.println(age1);
        // 获取父类的age属性,子类的age属性是String类型,需要用String接收
        String age2 = user.getAge(false);
        System.out.println(age2);


        Person person = new Person();
        // 把子类对象user的属性值赋值给person,即把user对象中存储的Person父类相
        // 关的属性值赋给父类对象person
        BeanUtils.copyProperties(user,person);

        System.out.println("person:" +person.toString());

        // 反向赋值,即把person的name设置为null后赋值给user,那么user对象属性中继承自父类的这个
        // 属性name也会被重新赋值为空,但它自己所特有的属性比如account、password不会受影响
        person.setName(null);
        BeanUtils.copyProperties(person,user);

        System.out.println("user:" +user.toString());
    }

    @Override
    public String toString() {
        return "User{" +
                "age(User)=" + age +
                ", age(Person)=" + super.getAge() +
                ", account='" + account + '\'' +
                ", password='" + password + '\'' +
                ", name(Person)='" + super.getName() + '\'' +
                '}';
    }
}

如上,我们创建的user对象,我们给这个对象的全部属性做赋值,注意这里user对象的全部属性 = user对象自己特有的属性(age(Interger类型),account,password) + 继承自父类Parent中的属性(id,name,age(String类型),sex)

我们把子类对象user的属性值赋值给person,即把user对象中存储的Person父类相关的属性值赋给父类对象person

Person person = new Person();//
BeanUtils.copyProperties(user,person);

结果

person:Person{id='1', name='wzh', age='23', sex='null'}

反向赋值,即把person的name设置为null后赋值给user,那么user对象属性中继承自父类的这个属性name被赋值为null,但它自己所特有的属性比如account、password不会受影响

person.setName(null);
BeanUtils.copyProperties(person,user);

情况三(带有POJO/Collection类型的属性)

Life类,表示是否存活

@Data
@AllArgsConstructor
public class Life {
    private String status; //取值dead是死亡,取值alived是存活
}

分别给User类和Person类装配这个life属性,而后测试代码如下

Life life = new Life("living");
person.setLife(life);

BeanUtils.copyProperties(person,user);

System.out.println("person life:" + person.getLife() + ","
        + "user life:" + user.getLife());

最终打印

person life:Life(status=living),user life:null

由此再次证明一个观点

BeanUtils.copyProperties(source,target);

如果source是父类,target是子类,那么最终只会把父类中的属性值同步给子类中继承自父类的属性,子类所独有的属性不会受到影响

去掉这两个类的继承关系,重新演示

 User user = new User();
 Person person = new Person();
 Life personLife = new Life("person living");
 Life userLeft = new Life("son living");
 person.setLife(personLife);
 user.setLife(userLeft);


 BeanUtils.copyProperties(person,user);

 System.out.println("person life:" + person.getLife() + ","
         + "user life:" + user.getLife() );
 System.out.println(person.getLife() == user.getLife());

打印

person life:Life(status=living),user life:Life(status=living)
true

赋值成功,而且同时可以看出BeanUtils.copyProperties中引用类型的属性间的拷贝方式是浅拷贝,浅拷贝即仅仅是把源对象person中life属性的引用赋值给目标对象user的life属性,而且就算目标对象user本身就具有life属性值,也会在copyProperties的过程中被源对象person中life对象覆盖它原本的life对象这个属性

情况四(两个带有泛型属性的类做属性拷贝)

@Data
public class Person<T> {
    private String id;
    private String name;
    private String age;
    private String sex;

    private List<T> list;
}
package com.example.demo2.test.gneric;

import com.example.demo2.mode.Life;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;

import java.util.ArrayList;
import java.util.List;

@Data
@Slf4j
public class User<T> {
    private Life life;

    private String account;
    private String password;

    private List<T> list;




    public static void main(String[] args) {
        User user = new User();
        Person person = new Person();


        List<Integer> list1 = new ArrayList<>();
        list1.add(1);
        list1.add(2);
        list1.add(3);


        List<String> list2 = new ArrayList<String>();
        list2.add("A");
        list2.add("B");
        list2.add("C");

        user.setList(list1);
        person.setList(list2);

        BeanUtils.copyProperties(user,person);

        log.info("user:{}",user);
        log.info("person:{}",person);
        
        // 结果为true,可见赋值的时候直接是把list1的引用直接赋值给了list2
        log.info("isEqual:{}",person.getList() == user.getList());
        
        // list2没有变化,可见我们没法直接对list列表间做赋值
        BeanUtils.copyProperties(list1,list2);
        log.info("list2:{}",list2);
    }
}

情况分析
泛型集合的作用是在编译期,过了编译期,泛型的影响会通过泛型擦除机制被立马被擦除掉,在程序运行期间即不再有实际意义,而我们分析源码可在,属性拷贝是利用的反射机制,即在程序运行期间通过getter/setter来做属性拷贝的,所以泛型对属性拷贝没有影响

结果分析
list属性被成功拷贝,同时我们发现person.getList和user.getList()完全是同一个list对象,可见赋值的时候直接是把list1的引用直接赋值给了list2,尝试把list1赋值给list2,最终结果list2没有变化,可见我们没法直接直接对list列表间做赋值

14:28:43.065 [main] INFO com.example.demo2.test.gneric.User - user:User(life=null, account=null, password=null, list=[1, 2, 3])
14:28:43.073 [main] INFO com.example.demo2.test.gneric.User - person:Person(id=null, name=null, age=null, sex=null, list=[1, 2, 3])
14:28:43.073 [main] INFO com.example.demo2.test.gneric.User - isEqual:true
14:28:43.105 [main] INFO com.example.demo2.test.gneric.User - list2:[A, B, C, D]
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

BeanUtils.copyProperties的用法(超详细,建议收藏) 的相关文章

随机推荐

  • GORM 基础 -- Gen

    https gorm io gen github 1 GEN Guides GEN 友好和更安全的代码生成 1 1 概述 来自动态原始SQL的惯用和可重用API 100 类型安全的DAO API 不使用 interface Database
  • printf(“%d,%d\n“,i--,i++)

    sample cpp include
  • Windows 下创建定时任务执行Python脚本

    文章目录 一 环境 二 脚本 三 创建定时任务 1 打开 任务计划程序 2 打开 创建任务 窗口 3 创建任务一一常规 4 创建任务一一触发器 5 创建任务一一操作 6 创建任务一一条件 7 创建任务一一设置 8 完成任务创建 四 验证定时
  • 记录自己在结构光三维重建领域的学习过程(一)

    仿真数据集与真是数据集之间差异较大 二者的网络均不可完美预测另一种数据 寻找解决办法 首先确定是不是数据的问题 阅读论文 Light field structured light projection data generation wit
  • 关于存储过程中SQL语句IN条件传参注意说

    背景说明 在数据库操作中我们经常会用到查询语句 在一些情况下 需要使用到IN条件 正常的查询中IN需要注意的是最好in中的参数不能超过1000个 超过1000的时候oracle会抛出异常 这个如何处理先不提 这次要说的是 如果在存储过程中使
  • 某单位分配到一个地址块 136.23.12.64/26。现在需要进一步划分为4个一样大的子网。试问:

    1 每个子网的网络前缀有多长 2 每个子网中有多少个地址 3 每个子网的地址块是什么 4 每一个子网可分配给主机使用的最小地址和最大地址是什么 姐
  • JS中的邮箱验证

    通过js在前端对用户输入进行校验 即可以产生较好的交互体验 也可以减轻后台的压力 邮箱的基本格式要求 1 只能以单词字符开头 即a z A Z 0 9 2 只能有一个 3 后面有一个到多个点 并且点不能在最后 4 特殊字符不能开头和结尾 使
  • 数据存储,详细讲解

    数据存储 详细讲解 数据类型的介绍 整形的内存存储 大小端介绍 浮点数的存储 数据类型的介绍 1 内置类型 char 字符数据类型 1 short 短整型 2 int 整形 4 long 长整型 4 8 long long 更长的整形 8
  • matlab之数组反序输出

    a 1 2 3 4 5 a end 1 1 5 4 3 2 1 转载于 https www cnblogs com yibeimingyue p 11201805 html
  • 高阶数据结构之并查集

    文章目录 并查集 并查集的常规实现 并查集的简化实现 算法题 模板 朴素的并查集 维护size的并查集 维护到祖宗节点的并查集 并查集 在某些应用问题中 需要将n个不同的元素划分成一些不想交的集合 开始时 每个元素自成一个单元集合 然后按照
  • 一文搞懂状态模式

    原理 状态机有三个组成部分 状态 事件 动作 遇到不同的事件会触发状态的转移和动作的执行 不过动作不是必须的 可能只有状态的转移 没有动作的执行 状态模式的目的就是实现状态机 案例带入 比如 超级马里奥 在游戏中 马里奥可以变身为多种形态
  • 抖音究竟对线下生意有没有价值?

    戳上方增长黑盒Growthbox点亮星标 深度研究不错过 主笔 邹小困 研究员 彬超 Emma 出品 增长黑盒研究组 01 防火防盗防抖音 互联网世界的后起之秀字节跳动 如同进击的巨人 在短短几年内就已将业务的触角伸向各个领域 图为chat
  • Java基础(IO输入输出流,读写文件操作,对象序列化)

    IO 输入输出流基本概念 输入流 信息从程序空间之外的地方流入程序空间里面 输出流 信息从程序空间里面流到程序空间之外的地方 输入输出流的分类 从流的方法划分 输入流 输入流 从流的分工划分 节点流 访问文件进行输入输出操作的流 处理流 在
  • IDEA常见错误:程序包org.apache.commons.xxx不存在

    具体错误 Could not transfer artifact org apache commons commons text pom 1 1 from to Nexus Repository Nexus Repository Manag
  • 全新【Photoshop 2022 for mac】中文版发布下载 支持M1 M1X M2处理器 详细安装教程

    Adobe PhotoShop 2022 for mac 23 0 是 Adobe 2022 中最重要的组成软件之一 PhotoShop主要用来处理图像 是行业内最流行的图像处理软件 ps 2022 更加智能化和强大 Adobe Photo
  • CAN db++(创建DBC文件)学习笔记

    CAN db 学习笔记 1 新建文件 2 模块介绍 3 建立DBC的过程 自下而上 信号 报文 节点 1 信号 2 报文 3 建立报文与信号的关系 4 建立信号在报文里的位置 因为前面定义了报文的数据长度 且把信号放到了报文里面 那么信号在
  • 【Linux_CentOS7】 systemctl restart named报错-Job for named.service failed because the control process

    14天阅读挑战赛 努力是为了不平庸 算法学习有些时候是枯燥的 这一次 让我们先人一步 趣学算法 欢迎记录下你的那些努力时刻 算法学习知识点 算法题解 遇到的算法bug 等等 在分享的同时加深对于算法的理解 同时吸收他人的奇思妙想 一起见证技
  • Taro+vue3小程序开启分享他人和分享到朋友圈

    import Taro useShareAppMessage useShareTimeline from tarojs taro onMounted gt Taro showShareMenu withShareTicket true me
  • 有趣的java代码_求一些有趣的java小程序?

    不请自来 用eclipse写的一个输出爱心的小程序 应该算是比较有趣的吧 下面是程序内容 public class love public static void main String args TODO Auto generated m
  • BeanUtils.copyProperties的用法(超详细,建议收藏)

    常见场景 我们如果有两个具有很多相同属性名的JavaBean对象a和b 想把a中的属性赋值到b 例如 接口中将接收到的前端请求参数XxxReqVo 我们想把这个入参转化为XxxQuery对象作为数据库的查询条件对象 传统做法是手动set 即