java中BeanUtils.copyProperties的用法(超詳細)
常見場景
我們?nèi)绻袃蓚€具有很多相同屬性名的JavaBean對象a和b,想把a中的屬性賦值到b,例如
- 接口中將接收到的前端請求參數(shù)XxxReqVo,我們想把這個入?yún)⑥D(zhuǎn)化為XxxQuery對象作為數(shù)據(jù)庫的查詢條件對象
傳統(tǒng)做法是手動set,即
XxxQuery xxxQuery = new XxxQuery(); xxxQuery .setAxx(xxxReqVo.getAxx()); xxxQuery .setBxx(xxxReqVo.getBxx()); xxxQuery .setCxx(xxxReqVo.getCxx());
如果有幾十個需要賦值的的字段呢?那就很頭疼了org.springframework.beans.BeanUtils,它提供了對java反射和自省API的包裝。它里面還有很多工具類,這里我們介紹一下該類下面的copyProperties方法,該工具方法可以幫我們大大簡化這一步
應(yīng)用案例
案例一(兩個無關(guān)的類做屬性拷貝)
@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); } }
結(jié)果
Person(id=1, name=wzh, age=2, sex=null)
通過上述測試我們就可以總結(jié)出相關(guān)結(jié)論,基本用法為
BeanUtils.copyProperties(source,target);
相當(dāng)于把源對象source的屬性值賦給目標(biāo)對象target中與源對象source的中有著同屬性名的屬性,如上述案例中Person作為目標(biāo)對象與源對象User中有著共同的同名屬性id,name,age,所以person中的這三個字段被賦值成功,賦值的數(shù)據(jù)來源正是user對象,sex這個字段是Person類所特有的,所以不會被賦值,同時還要特別注意賦值操作相關(guān)類的屬性一定要有對應(yīng)的setter/getter,即
- 源對象source的屬性拷貝值賦給目標(biāo)對象target的過程中,屬性名和屬性類型都相同的屬性才能被成功拷貝賦值,例如id,name,age這三個目標(biāo)對象的屬性被賦值成功,目標(biāo)對象中的sex屬性,由于源對象中沒有同名的屬性所以沒法被賦值成功。
- 做賦值的屬性一定要有對應(yīng)的setter/getter才能成功賦值
案例二(父子類之間做賦值)
這里難度升級一下,我們不僅僅要演示父子類之間做賦值,還要加一個特征,即在父類Person具有String類型的age屬性的基礎(chǔ)上,子類中定義一個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,類內(nèi)含有測試用的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(); } //重載:方法名相同,但返回值數(shù)目/類型/不同類型返回值的順序非一致, // 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"); // 設(shè)置父類的age屬性值 user.setAge("23"); // 設(shè)置子類的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父類相 // 關(guān)的屬性值賦給父類對象person BeanUtils.copyProperties(user,person); System.out.println("person:" +person.toString()); // 反向賦值,即把person的name設(shè)置為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() + '\'' + '}'; } }
如上,我們創(chuàng)建的user對象,我們給這個對象的全部屬性做賦值,注意這里user對象的全部屬性 = user對象自己特有的屬性(age(Interger類型),account,password) + 繼承自父類Parent中的屬性(id,name,age(String類型),sex)
我們把子類對象user的屬性值賦值給person,即把user對象中存儲的Person父類相關(guān)的屬性值賦給父類對象person
Person person = new Person();// BeanUtils.copyProperties(user,person);
結(jié)果
person:Person{id='1', name='wzh', age='23', sex='null'}
反向賦值,即把person的name設(shè)置為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是子類,那么最終只會把父類中的屬性值同步給子類中繼承自父類的屬性,子類所獨有的屬性不會受到影響
去掉這兩個類的繼承關(guān)系,重新演示
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中l(wèi)ife屬性的引用賦值給目標(biāo)對象user的life屬性,而且就算目標(biāo)對象user本身就具有l(wèi)ife屬性值,也會在copyProperties的過程中被源對象person中l(wèi)ife對象覆蓋它原本的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); // 結(jié)果為true,可見賦值的時候直接是把list1的引用直接賦值給了list2 log.info("isEqual:{}",person.getList() == user.getList()); // list2沒有變化,可見我們沒法直接對list列表間做賦值 BeanUtils.copyProperties(list1,list2); log.info("list2:{}",list2); } }
情況分析泛型集合的作用是在編譯期,過了編譯期,泛型的影響會通過泛型擦除機制被立馬被擦除掉,在程序運行期間即不再有實際意義,而我們分析源碼可在,屬性拷貝是利用的反射機制,即在程序運行期間通過getter/setter來做屬性拷貝的,所以泛型對屬性拷貝沒有影響
結(jié)果分析list屬性被成功拷貝,同時我們發(fā)現(xiàn)person.getList和user.getList()完全是同一個list對象,可見賦值的時候直接是把list1的引用直接賦值給了list2,嘗試把list1賦值給list2,最終結(jié)果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]
到此這篇關(guān)于java中BeanUtils.copyProperties的用法(超詳細)的文章就介紹到這了,更多相關(guān)java BeanUtils.copyProperties內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實現(xiàn)讀取鍵盤輸入保存到txt文件,再統(tǒng)計并輸出每個單詞出現(xiàn)次數(shù)的方法
這篇文章主要介紹了Java實現(xiàn)讀取鍵盤輸入保存到txt文件,再統(tǒng)計并輸出每個單詞出現(xiàn)次數(shù)的方法,涉及java文件I/O操作及字符串遍歷、運算實現(xiàn)統(tǒng)計功能相關(guān)技巧,需要的朋友可以參考下2017-07-07Java中try-catch-finally執(zhí)行順序你知道嗎
本文主要介紹了try-catch-finally執(zhí)行順序你知道嗎,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06RxJava中map和flatMap的用法區(qū)別源碼解析
這篇文章主要為大家介紹了RxJava中map和flatMap的用法區(qū)別源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09SpringBoot如何使用validator框架優(yōu)雅地校驗參數(shù)
文章介紹了如何使用SpringValidation進行參數(shù)校驗,包括引入依賴、@requestBody和@requestParam參數(shù)校驗、統(tǒng)一異常處理、分組校驗、嵌套校驗、自定義校驗、業(yè)務(wù)規(guī)則校驗以及@Valid和@Validated的區(qū)別,同時,列舉了常用的BeanValidation和HibernateValidator注解2025-02-02java基礎(chǔ)之?dāng)?shù)組常用操作總結(jié)(必看篇)
下面小編就為大家?guī)硪黄猨ava基礎(chǔ)之?dāng)?shù)組常用操作總結(jié)(必看篇)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06Springboot接入MyBatisPlus的實現(xiàn)
最近web端比較熱門的框架就是SpringBoot和Mybatis-Plus,這里簡單總結(jié)集成用法,具有一定的參考價值,感興趣的可以了解一下2023-09-09