java中BeanUtils.copyProperties的用法(超详细)
作者:生命不息战斗不止(王子晗)
常见场景
我们如果有两个具有很多相同属性名的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]
到此这篇关于java中BeanUtils.copyProperties的用法(超详细)的文章就介绍到这了,更多相关java BeanUtils.copyProperties内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!