關于BeanUtils.copyProperties(source, target)的使用
BeanUtils.copyProperties
首先,使用的是org.springframework.beans.BeanUtils;
source 來源, target 目標
顧名思義, BeanUtils.copyProperties(source, target); 第一個參數(shù)是需要拷貝的目標,第二個參數(shù)是拷貝后的目標。
因為這個方法有很多種情況,容易分不清,所以今天測了一下不同情況下的結果如何。
1.target里面有source里沒有的屬性
并且此屬性有值時
2.target和source相同屬性的值不一樣時
下面是沒有拷貝之前的值

拷貝之后

可以看到,target里面不同值并沒有清空,而是保留了下來。而相中屬性本身存在的值被覆蓋。
3.當target和source里面的屬性名相同而類型不同時

拷貝之后

類型不同的屬性無法拷貝。
Spring自帶BeanUtils.copyProperties(Object source, Object target)之坑
在java服務化項目中,客戶端和服務端之間交互經(jīng)常用到BeanCopy,其目的是為了方便類之間的賦值,簡單方便,但是經(jīng)常會遇到復合對象賦值不上去的情況,究其原因是對BeanUtils.copyProperties(Object source, Object target)方法底層源碼的不了解導致的,下面我來一步一步解釋其原因。
先看一個例子:
@Data
public class BdmTeamMonthNewStoreTopResult implements Serializable {
private static final long serialVersionUID = -3251482519506276368L;
/**
* 排名列表
*/
private List<BdmTeamMonthNewStoreTopInfo> topInfoList;
/**
* 我的排名信息
*/
private BdmTeamMonthNewStoreTopMyInfo myTopInfo;
@Override
public String toString() {
return ReflectionToStringBuilder.reflectionToString(this,
ToStringStyle.SHORT_PREFIX_STYLE);
}
}
@Data
public class MonthNewStoreTopInfoResponse implements Serializable {
private static final long serialVersionUID = 4483822161951780674L;
/**
* 排名信息列表
*/
private List<MonthNewStoreTopInfo> topInfoList;
/**
* 我的排名信息
*/
private MonthNewStoreTopMyInfo myTopInfo;
@Override
public String toString() {
return ReflectionToStringBuilder.reflectionToString(this,
ToStringStyle.SHORT_PREFIX_STYLE);
}
}
當我們用BeanUtils.copyProperties(monthNewStoreTopResponse , bdmTeamMonthNewStoreTopResult )時會發(fā)現(xiàn)myTopInfo這個對象賦值為null,這是為什么呢?讓我們來看一看源碼:
//這是點進源碼的第一段代碼
public static void copyProperties(Object source, Object target) throws BeansException {
copyProperties(source, target, null, (String[]) null);
}
//這個才是copy的主代碼
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
@Nullable String... ignoreProperties) throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
}
catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
}
其中ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())這個校驗起到了關鍵的作用,我們再進入這段代碼的源碼看一眼,源碼如下:
public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) {
Assert.notNull(lhsType, "Left-hand side type must not be null");
Assert.notNull(rhsType, "Right-hand side type must not be null");
if (lhsType.isAssignableFrom(rhsType)) {
return true;
}
if (lhsType.isPrimitive()) {
Class<?> resolvedPrimitive = primitiveWrapperTypeMap.get(rhsType);
if (lhsType == resolvedPrimitive) {
return true;
}
}
else {
Class<?> resolvedWrapper = primitiveTypeToWrapperMap.get(rhsType);
if (resolvedWrapper != null && lhsType.isAssignableFrom(resolvedWrapper)) {
return true;
}
}
return false;
}
其中l(wèi)hsType.isAssignableFrom(rhsType)判定此 Class 對象所表示的類或接口與指定的 Class 參數(shù)所表示的類或接口是否相同,或是否是其超類或超接口。
如果是則返回 true;否則返回 false。
如果該 Class表示一個基本類型,且指定的 Class 參數(shù)正是該 Class 對象,則該方法返回 true;否則返回 false。
意思其實就是說lhsType是不是rhsType的子類,如果是,則返回true,否則返回false。
這也就是說我們上面的例子MonthNewStoreTopMyInfo 對象和我們將要賦值的對象BdmTeamMonthNewStoreTopMyInfo 是同一個對象或者是它的子類才可以copy賦值成功,否則直接跳過返回了,不進行writeMethod.invoke(target, value)賦值;
哪為什么topInfoList卻能賦值成功呢?
因為在lhsType.isAssignableFrom(rhsType)校驗的時候是判斷的是List類型的子類而不是List<BdmTeamMonthNewStoreTopMyInfo>中的BdmTeamMonthNewStoreTopMyInfo的子類。
所以我們在用Spring自帶BeanUtils.copyProperties(Object source, Object target)進行對象copy時候需要特別注意,如果變量為非java自帶的對象類型,則需要注意復合對象中的變量對象和被拷貝變量對象是同類型才可以。
如果MonthNewStoreTopMyInfo 和BdmTeamMonthNewStoreTopMyInfo不是同一個類型,可以通過先獲得MonthNewStoreTopMyInfo 這個對象再和需要賦值的對象BdmTeamMonthNewStoreTopMyInfo進行變量級別的調用BeanUtils.copyProperties(MonthNewStoreTopMyInfo , BdmTeamMonthNewStoreTopMyInfo),最后再把復制后的結果set進結果集。
最好的解決辦法是創(chuàng)建一個公共的model對象,替換MonthNewStoreTopMyInfo和BdmTeamMonthNewStoreTopMyInfo,這樣也少創(chuàng)建了一個類,同時也減少了代碼量,維護一份model,當有新增需求變化時,只需要修改公共的model對象即可,簡單方便。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
springboot?vue接口測試HutoolUtil?TreeUtil處理樹形結構
這篇文章主要介紹了springboot?vue接口測試HutoolUtil?TreeUtil處理樹形結構,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05
Spring項目如何實現(xiàn)帶請求鏈路id的日志記錄
我們在做項目的時候通常需要通過請求日志來排查定位線上問題,如果日志比較多而我們又需要查找整個請求的全部日志的時候會比較困難,下面我們就來看看如何用java aop實現(xiàn)請求id的日志記錄吧2024-12-12
SpringBoot+Vue項目部署上線的實現(xiàn)示例
本文主要介紹了SpringBoot+Vue項目部署上線的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-02-02
java中用float時,數(shù)字后面加f,這樣是為什么你知道嗎
這篇文章主要介紹了java用float時,數(shù)字后面加f,這樣是為什么你知道嗎?具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
Java后臺與微信小程序的數(shù)據(jù)交互實現(xiàn)
這篇文章主要介紹了Java后臺與微信小程序的數(shù)據(jù)交互實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12

