java 对象复制字段_利用Java反射机制实现对象相同字段的复制
一。如何實(shí)現(xiàn)不同類型對(duì)象之間的復(fù)制問(wèn)題?
1、為什么會(huì)有這個(gè)問(wèn)題?
近來(lái)在進(jìn)行一個(gè)項(xiàng)目開發(fā)的時(shí)候,為了隱藏后端數(shù)據(jù)庫(kù)表結(jié)構(gòu)、同時(shí)也為了配合給前端一個(gè)更友好的API接口文檔(swagger API文檔),我采用POJO來(lái)對(duì)應(yīng)數(shù)據(jù)表結(jié)構(gòu),使用VO來(lái)給傳遞前端要展示的數(shù)據(jù),同時(shí)使用DTO來(lái)進(jìn)行請(qǐng)求參數(shù)的封裝。以上是一個(gè)具體的場(chǎng)景,可以發(fā)現(xiàn)這樣子一個(gè)現(xiàn)象:POJO、VO、DTO對(duì)象是同一個(gè)數(shù)據(jù)的不同視圖,所以會(huì)有很多相同的字段,由于不同的地方使用不同的對(duì)象,無(wú)可避免的會(huì)存在對(duì)象之間的值遷移問(wèn)題,遷移的一個(gè)特征就是需要遷移的值字段相同。字段相同,于是才有了不同對(duì)象之間進(jìn)行值遷移復(fù)制的問(wèn)題。
2、現(xiàn)有的解決方法
一個(gè)一個(gè)的get出來(lái)后又set進(jìn)去。這個(gè)方法無(wú)可避免會(huì)增加很多的編碼復(fù)雜度,還是一些很沒(méi)有營(yíng)養(yǎng)的代碼,看多了還會(huì)煩,所以作為一個(gè)有點(diǎn)小追求的程序員都沒(méi)有辦法忍受這種摧殘。
使用別人已經(jīng)存在的工具。在spring包里面有一個(gè)可以復(fù)制對(duì)象屬性的工具方法,可以進(jìn)行對(duì)象值的復(fù)制,下一段我們?cè)敿?xì)去分析它的這個(gè)工具方法。
自己動(dòng)手豐衣足食。自己造工具來(lái)用,之所以自己造工具不是因?yàn)橄矚g造工具,而是現(xiàn)有的工具沒(méi)辦法解決自己的需求,不得已而為之。
二、他山之石可以攻玉,詳談spring的對(duì)象復(fù)制工具
1、看看spring的對(duì)象復(fù)制工具到底咋樣?
類名:org.springframework.beans.BeanUtils
這個(gè)類里面所有的屬性復(fù)制的方法都調(diào)用了同一個(gè)方法,我們就直接分析這個(gè)原始的方法就行了。
/**
* Copy the property values of the given source bean into the given target bean.
*
Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
* @param source the source bean:也就是說(shuō)要從這個(gè)對(duì)象里面復(fù)制值出去
* @param target the target bean:出去就是復(fù)制到這里面來(lái)
* @param editable the class (or interface) to restrict property setting to:這個(gè)類對(duì)象是target的父類或其實(shí)現(xiàn)的接口,用于控制屬性復(fù)制的范圍
* @param ignoreProperties array of property names to ignore:需要忽略的字段
* @throws BeansException if the copying failed
* @see BeanWrapper
*/
private static void copyProperties(Object source, Object target, Class> editable, String... ignoreProperties)
throws BeansException {
//這里在校驗(yàn)要復(fù)制的對(duì)象是不可以為null的,這兩個(gè)方法可是會(huì)報(bào)錯(cuò)的!!
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
//這里和下面的代碼就有意思了
Class> actualEditable = target.getClass();//獲取目標(biāo)對(duì)象的動(dòng)態(tài)類型
//下面判斷的意圖在于控制屬性復(fù)制的范圍
if (editable != null) {
//必須是target對(duì)象的父類或者其實(shí)現(xiàn)的接口類型,相當(dāng)于instanceof運(yùn)算符
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
//不得不說(shuō),下面這段代碼乖巧的像綿羊,待我們來(lái)分析分析它是如何如何乖巧的
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);//獲取屬性描述,描述是什么?描述就是對(duì)屬性的方法信息的封裝,好乖。
List ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
//重頭戲開始了!開始進(jìn)行復(fù)制了
for (PropertyDescriptor targetPd : targetPds) {
//先判斷有沒(méi)有寫方法,沒(méi)有寫方法我也就沒(méi)有必要讀屬性出來(lái)了,這個(gè)懶偷的真好!
Method writeMethod = targetPd.getWriteMethod();
//首先,沒(méi)有寫方法的字段我不寫,乖巧撒?就是說(shuō)你不讓我改我就不改,讓我忽略我就忽略!
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
//如果沒(méi)辦法從原對(duì)象里面讀出屬性也沒(méi)有必要繼續(xù)了
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
//這里就更乖巧了!寫方法不讓我寫我也不寫!!!
if (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
//這里就算了,來(lái)都來(lái)了,就乖乖地進(jìn)行值復(fù)制吧,別搞東搞西的了
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);
}
}
}
}
}
}
2、對(duì)復(fù)制工具的一些看法和總結(jié)
總結(jié)上一段代碼的分析,我們發(fā)現(xiàn)spring自帶的工具有以下特點(diǎn):
它名副其實(shí)的是在復(fù)制屬性,而不是字段!!
它可以通過(guò)一個(gè)目標(biāo)對(duì)象的父類或者其實(shí)現(xiàn)的接口來(lái)控制需要復(fù)制屬性的范圍
很貼心的可以忽略原對(duì)象的某些字段,可以通過(guò)2的方法忽略某些目標(biāo)對(duì)象的字段
但是,這遠(yuǎn)遠(yuǎn)不夠!!!我需要如下的功能:
復(fù)制對(duì)象的字段,而不是屬性,也就是說(shuō)我需要一個(gè)更暴力的復(fù)制工具。
我需要忽略原對(duì)象的某些字段,同時(shí)也能夠忽略目標(biāo)對(duì)象的某些字段。
我的項(xiàng)目還需要忽略原對(duì)象為null的字段和目標(biāo)對(duì)象不為null的字段
帶著這三個(gè)需求,開始我的工具制造。
總結(jié)
以上是生活随笔為你收集整理的java 对象复制字段_利用Java反射机制实现对象相同字段的复制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java swing web_Java-
- 下一篇: java修改默认字符编码_设置默认的Ja