溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

利用Java反射機(jī)制實(shí)現(xiàn)對(duì)象相同字段的復(fù)制操作

發(fā)布時(shí)間:2020-08-28 20:40:41 來(lái)源:腳本之家 閱讀:305 作者:一只有著大國(guó)夢(mèng)的兔子 欄目:開(kāi)發(fā)技術(shù)

一、如何實(shí)現(xiàn)不同類型對(duì)象之間的復(fù)制問(wèn)題?

1、為什么會(huì)有這個(gè)問(wèn)題?

近來(lái)在進(jìn)行一個(gè)項(xiàng)目開(kāi)發(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.
 * <p>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<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

 //重頭戲開(kāi)始了!開(kāi)始進(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ù)制屬性,而不是字段?。?/p>

它可以通過(guò)一個(gè)目標(biāo)對(duì)象的父類或者其實(shí)現(xiàn)的接口來(lái)控制需要復(fù)制屬性的范圍

很貼心的可以忽略原對(duì)象的某些字段,可以通過(guò)2的方法忽略某些目標(biāo)對(duì)象的字段

但是,這遠(yuǎn)遠(yuǎn)不夠?。。∥倚枰缦碌墓δ埽?/p>

復(fù)制對(duì)象的字段,而不是屬性,也就是說(shuō)我需要一個(gè)更暴力的復(fù)制工具。

我需要忽略原對(duì)象的某些字段,同時(shí)也能夠忽略目標(biāo)對(duì)象的某些字段。

我的項(xiàng)目還需要忽略原對(duì)象為null的字段和目標(biāo)對(duì)象不為null的字段

帶著這三個(gè)需求,開(kāi)始我的工具制造。

三、自己動(dòng)手豐衣足食

1、我需要解析字節(jié)碼

為了避免對(duì)字節(jié)碼的重復(fù)解析,使用緩存來(lái)保留解析過(guò)的字節(jié)碼解析結(jié)果,同時(shí)為了不讓這個(gè)工具太占用內(nèi)存,使用軟引用來(lái)進(jìn)行緩存,上代碼:

 /*
  ******************************************************
  * 基礎(chǔ)的用于支持反射解析的解析結(jié)果緩存,使用軟引用實(shí)現(xiàn)
  ******************************************************
  */
 private static final Map<Class<?>,SoftReference<Map<String,Field>>> resolvedClassCache = new ConcurrentHashMap<>();
 
 /**
  * 同步解析字節(jié)碼對(duì)象,將解析的結(jié)果放入到緩存 1、解析后的字段對(duì)象全部 accessAble
  * 1、返回的集合不支持修改,要修改請(qǐng)記得自己重新建一個(gè)復(fù)制的副本
  * @param sourceClass:需要解析的字節(jié)碼對(duì)象
  */
 @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
 public static Map<String,Field> resolveClassFieldMap(final Class<?> sourceClass){

  SoftReference<Map<String,Field>> softReference = resolvedClassCache.get(sourceClass);

  //判斷是否已經(jīng)被初始化
  if(softReference == null || softReference.get() == null){

   //對(duì)同一個(gè)字節(jié)碼對(duì)象的解析是同步的,但是不同字節(jié)碼對(duì)象的解析是并發(fā)的,因?yàn)樽止?jié)碼對(duì)象只有一個(gè)
   synchronized(sourceClass){

    softReference = resolvedClassCache.get(sourceClass);

    if(softReference == null || softReference.get() == null){

    //采用:<字段名稱,字段對(duì)象> 來(lái)記錄解析結(jié)果
     Map<String,Field> fieldMap = new HashMap<>();

     /*
     Returns an array of Field objects reflecting all the fields declared by the class or interface represented by this
     Class object. This includes public, protected, default access, and private fields, but excludes inherited fields
     */
     Field[] declaredFields = sourceClass.getDeclaredFields();

     if(declaredFields != null && declaredFields.length > 0){

      for(Field field : declaredFields){

       /*
       Set the accessible flag for this object to the indicated boolean value.
       */
       field.setAccessible(true);
   //字段名稱和字段對(duì)象
       fieldMap.put(field.getName(),field);
      }
     }

     //設(shè)置為不變Map,這個(gè)肯定是不能夠改的?。∷匀〉臅r(shí)候需要重新構(gòu)建一個(gè)map
     fieldMap = Collections.unmodifiableMap(fieldMap);

     softReference = new SoftReference<>(fieldMap);

     /*
     更新緩存,將解析后的數(shù)據(jù)加入到緩存里面去
      */
     resolvedClassCache.put(sourceClass,softReference);

     return fieldMap;
    }
   }
  }

  /*
  運(yùn)行到這里來(lái)的時(shí)候要么早就存在,要么就是已經(jīng)被其他的線程給初始化了
   */
  return softReference.get();
 }

2、我需要能夠進(jìn)行對(duì)象的復(fù)制,基本方法

 /**
  * 進(jìn)行屬性的基本復(fù)制操作
  * @param source:源對(duì)象
  * @param sourceFieldMap:原對(duì)象解析結(jié)果
  * @param target:目標(biāo)對(duì)象
  * @param targetFieldMap:目標(biāo)對(duì)象解析結(jié)果
  */
 public static void copyObjectProperties(Object source,Map<String,Field> sourceFieldMap,Object target,Map<String,Field> targetFieldMap){

  //進(jìn)行屬性值復(fù)制
  sourceFieldMap.forEach(
    (fieldName,sourceField) -> {

     //查看目標(biāo)對(duì)象是否存在這個(gè)字段
     Field targetField = targetFieldMap.get(fieldName);

     if(targetField != null){

      try{
       //對(duì)目標(biāo)字段進(jìn)行賦值操作
       targetField.set(target,sourceField.get(source));
      }catch(IllegalAccessException e){
       e.printStackTrace();
      }
     }
    }
  );
 }

3、夜深了,準(zhǔn)備睡覺(jué)了

基于這兩個(gè)方法,對(duì)其進(jìn)行封裝,實(shí)現(xiàn)了我需要的功能,并且在項(xiàng)目中運(yùn)行目前還沒(méi)有bug,應(yīng)該可以直接用在生產(chǎn)環(huán)境,各位看官覺(jué)得可以可以拿來(lái)試一試哦??!

4、完整的代碼(帶注釋:需要自取,無(wú)外部依賴,拿來(lái)即用)

package edu.cqupt.demonstration.common.util;

import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 反射的工具集,主要用于對(duì)對(duì)象的復(fù)制操作
 */
public class ReflectUtil {

 /*
  ******************************************************
  * 基礎(chǔ)的用于支持反射解析的解析結(jié)果緩存,使用軟引用實(shí)現(xiàn)
  ******************************************************
  */
 private static final Map<Class<?>,SoftReference<Map<String,Field>>> resolvedClassCache = new ConcurrentHashMap<>();

 /*
  ****************************************
  * 獲取一個(gè)對(duì)象指定條件字段名稱的工具方法
  ****************************************
  */

 /**
  * 獲取一個(gè)對(duì)象里面字段為null的字段名稱集合
  */
 public static String[] getNullValueFieldNames(Object source){

  //非空校驗(yàn):NullPointerException
  Objects.requireNonNull(source);

  Class<?> sourceClass = source.getClass();

  //從緩存里面獲取,如果緩存里面沒(méi)有就會(huì)進(jìn)行第一次反射解析
  Map<String,Field> classFieldMap = getClassFieldMapWithCache(sourceClass);

  List<String> nullValueFieldNames = new ArrayList<>();

  classFieldMap.forEach(
    (fieldName,field) -> {

     try{
      //挑選出值為null的字段名稱
      if(field.get(source) == null){
       nullValueFieldNames.add(fieldName);
      }
     }catch(IllegalAccessException e){
      e.printStackTrace();
     }
    }
  );

  return nullValueFieldNames.toArray(new String[]{});
 }

 /**
  * 獲取一個(gè)對(duì)象里面字段不為null的字段名稱集合
  */
 public static String[] getNonNullValueFieldNames(Object source){

   //非空校驗(yàn)
  Objects.requireNonNull(source);

  //獲取空值字段名稱
  String[] nullValueFieldNames = getNullValueFieldNames(source);

  Map<String,Field> classFieldMap = getClassFieldMapWithCache(source.getClass());

  //獲取全部的字段名稱,因?yàn)樵瓟?shù)據(jù)沒(méi)辦法修改,所以需要重新建立一個(gè)集合來(lái)進(jìn)行判斷
  Set<String> allFieldNames = new HashSet<>(classFieldMap.keySet());

  //移除掉值為null的字段名稱
  allFieldNames.removeAll(Arrays.asList(nullValueFieldNames));

  return allFieldNames.toArray(new String[]{});
 }

 /*
  ***************************************************************
  * 復(fù)制一個(gè)對(duì)象的相關(guān)工具方法,注意事項(xiàng)如下:
  * 1、只能復(fù)制字段名稱相同且數(shù)據(jù)類型兼容的字段數(shù)據(jù)
  * 2、只能復(fù)制這個(gè)對(duì)象實(shí)際類(運(yùn)行時(shí)動(dòng)態(tài)類型)里面聲明的各種字段
  ***************************************************************
  */

 /**
  * 將一個(gè)對(duì)象里面字段相同、類型兼容的數(shù)據(jù)復(fù)制到另外一個(gè)對(duì)象去
  * 1、只復(fù)制類的運(yùn)行時(shí)類型的聲明的全部訪問(wèn)權(quán)限的字段
  * @param source:從這個(gè)對(duì)象復(fù)制
  * @param target:復(fù)制到這個(gè)對(duì)象來(lái)
  */
 public static void copyPropertiesSimple(Object source,Object target){

  copyObjectProperties(
    source,new HashMap<>(getClassFieldMapWithCache(source.getClass())),
    target,new HashMap<>(getClassFieldMapWithCache(target.getClass())));
 }

 /**
  * 除實(shí)現(xiàn) copyPropertiesSimple 的功能外,會(huì)忽略掉原對(duì)象的指定字段的復(fù)制
  * @param ignoreFieldNames:需要忽略的原對(duì)象字段名稱集合
  */
 public static void copyPropertiesWithIgnoreSourceFields(Object source,Object target,String ...ignoreFieldNames){

  Map<String,Field> sourceFieldMap = new HashMap<>(getClassFieldMapWithCache(source.getClass()));

  filterByFieldName(sourceFieldMap,ignoreFieldNames);

  copyObjectProperties(source,sourceFieldMap,target,new HashMap<>(getClassFieldMapWithCache(target.getClass())));
 }

 /**
  * 除實(shí)現(xiàn) copyPropertiesSimple 的功能外,會(huì)忽略掉原對(duì)象字段值為null的字段
  */
 public static void copyPropertiesWithNonNullSourceFields(Object source,Object target){

  Map<String,Field> sourceFieldMap = new HashMap<>(getClassFieldMapWithCache(source.getClass()));

  filterByFieldValue(source,sourceFieldMap,true);

  copyObjectProperties(source,sourceFieldMap,target,new HashMap<>(getClassFieldMapWithCache(target.getClass())));
 }

 /**
  * 除實(shí)現(xiàn) copyPropertiesSimple 的功能外,會(huì)忽略掉目標(biāo)對(duì)象的指定字段的復(fù)制
  * @param ignoreFieldNames:需要忽略的原對(duì)象字段名稱集合
  */
 public static void copyPropertiesWithIgnoreTargetFields(Object source,Object target,String ...ignoreFieldNames){

  Map<String,Field> targetFieldMap = new HashMap<>(getClassFieldMapWithCache(target.getClass()));

  filterByFieldName(targetFieldMap,ignoreFieldNames);

  copyObjectProperties(source,new HashMap<>(getClassFieldMapWithCache(source.getClass())),target,targetFieldMap);
 }

 /**
  * 除實(shí)現(xiàn) copyPropertiesSimple 的功能外,如果目標(biāo)對(duì)象的屬性值不為null將不進(jìn)行覆蓋
  */
 public static void copyPropertiesWithTargetFieldNonOverwrite(Object source,Object target){

  Map<String,Field> targetFieldMap = new HashMap<>(getClassFieldMapWithCache(target.getClass()));

  filterByFieldValue(target,targetFieldMap,false);
  copyObjectProperties(source,new HashMap<>(getClassFieldMapWithCache(source.getClass())),target,targetFieldMap);
 }

 /**
  * 進(jìn)行復(fù)制的完全定制復(fù)制
  * @param source:源對(duì)象
  * @param target:目標(biāo)對(duì)象
  * @param ignoreSourceFieldNames:需要忽略的原對(duì)象字段名稱集合
  * @param ignoreTargetFieldNames:要忽略的目標(biāo)對(duì)象字段集合
  * @param isSourceFieldValueNullAble:是否在源對(duì)象的字段為null的時(shí)候仍然進(jìn)行賦值
  * @param isTargetFiledValueOverwrite:是否在目標(biāo)對(duì)象的值不為null的時(shí)候仍然進(jìn)行賦值
  */
 public static void copyPropertiesWithConditions(Object source,Object target
   ,String[] ignoreSourceFieldNames,String[] ignoreTargetFieldNames
   ,boolean isSourceFieldValueNullAble,boolean isTargetFiledValueOverwrite){

  Map<String,Field> sourceFieldMap = new HashMap<>(getClassFieldMapWithCache(source.getClass()));
  Map<String,Field> targetFieldMap = new HashMap<>(getClassFieldMapWithCache(target.getClass()));

  if(!isSourceFieldValueNullAble){

   filterByFieldValue(source,sourceFieldMap,true);
  }

  if(!isTargetFiledValueOverwrite){
   filterByFieldValue(target,targetFieldMap,false);
  }

  filterByFieldName(sourceFieldMap,ignoreSourceFieldNames);
  filterByFieldName(targetFieldMap,ignoreTargetFieldNames);
  copyObjectProperties(source,sourceFieldMap,target,targetFieldMap);
 }

 /*
  ******************************
  * 內(nèi)部工具方法或者內(nèi)部兼容方法
  ******************************
  */

 /**
  * 同步解析字節(jié)碼對(duì)象,將解析的結(jié)果放入到緩存 1、解析后的字段對(duì)象全部 accessAble
  * 1、返回的集合不支持修改,要修改請(qǐng)記得自己重新建一個(gè)復(fù)制的副本
  * @param sourceClass:需要解析的字節(jié)碼對(duì)象
  */
 @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
 public static Map<String,Field> resolveClassFieldMap(final Class<?> sourceClass){

  SoftReference<Map<String,Field>> softReference = resolvedClassCache.get(sourceClass);

  //判斷是否已經(jīng)被初始化
  if(softReference == null || softReference.get() == null){

   //對(duì)同一個(gè)字節(jié)碼對(duì)象的解析是同步的,但是不同字節(jié)碼對(duì)象的解析是并發(fā)的
   synchronized(sourceClass){

    softReference = resolvedClassCache.get(sourceClass);
    if(softReference == null || softReference.get() == null){

     Map<String,Field> fieldMap = new HashMap<>();
     /*
     Returns an array of Field objects reflecting all the fields declared by the class or interface represented by this
     Class object. This includes public, protected, default access, and private fields, but excludes inherited fields
     */
     Field[] declaredFields = sourceClass.getDeclaredFields();
     if(declaredFields != null && declaredFields.length > 0){
      for(Field field : declaredFields){
       /*
       Set the accessible flag for this object to the indicated boolean value.
       */
       field.setAccessible(true);
       fieldMap.put(field.getName(),field);
      }
     }

     //設(shè)置為不變Map
     fieldMap = Collections.unmodifiableMap(fieldMap);

     softReference = new SoftReference<>(fieldMap);

     /*
     更新緩存,將解析后的數(shù)據(jù)加入到緩存里面去
      */
     resolvedClassCache.put(sourceClass,softReference);

     return fieldMap;
    }
   }
  }
  /*
  運(yùn)行到這里來(lái)的時(shí)候要么早就存在,要么就是已經(jīng)被其他的線程給初始化了
   */
  return softReference.get();
 }

 /**
  * 確保正確的從緩存里面獲取解析后的數(shù)據(jù)
  * 1、返回的集合不支持修改,要修改請(qǐng)記得自己重新建一個(gè)復(fù)制的副本
  * @param sourceClass:需要解析的字節(jié)碼對(duì)象
  */
 public static Map<String,Field> getClassFieldMapWithCache(Class<?> sourceClass){

  //查看緩存里面有沒(méi)有已經(jīng)解析完畢的現(xiàn)成的數(shù)據(jù)
  SoftReference<Map<String,Field>> softReference = resolvedClassCache.get(sourceClass);

  //確保classFieldMap的正確初始化和緩存
  if(softReference == null || softReference.get() == null){

   //解析字節(jié)碼對(duì)象
   return resolveClassFieldMap(sourceClass);
  }else {

   //從緩存里面正確的取出數(shù)據(jù)
   return softReference.get();
  }
 }

 /**
  * 將一個(gè)可變參數(shù)集合轉(zhuǎn)換為L(zhǎng)ist集合,當(dāng)為空的時(shí)候返回空集合
  */
 public static <T> List<T> resolveArrayToList(T ...args){

  List<T> result = new ArrayList<>();
  if(args != null && args.length > 0){
   result = Arrays.asList(args);
  }
  return result;
 }

 /**
  * 進(jìn)行屬性的基本復(fù)制操作
  * @param source:源對(duì)象
  * @param sourceFieldMap:原對(duì)象解析結(jié)果
  * @param target:目標(biāo)對(duì)象
  * @param targetFieldMap:目標(biāo)對(duì)象解析結(jié)果
  */
 public static void copyObjectProperties(Object source,Map<String,Field> sourceFieldMap,Object target,Map<String,Field> targetFieldMap){

  //進(jìn)行屬性值復(fù)制
  sourceFieldMap.forEach(
    (fieldName,sourceField) -> {

     //查看目標(biāo)對(duì)象是否存在這個(gè)字段
     Field targetField = targetFieldMap.get(fieldName);

     if(targetField != null){

      try{
       //對(duì)目標(biāo)字段進(jìn)行賦值操作
       targetField.set(target,sourceField.get(source));
      }catch(IllegalAccessException e){
       e.printStackTrace();
      }
     }
    }
  );
 }

 /**
  * 忽略掉對(duì)象里面的某些字段
  */
 public static void filterByFieldName(Map<String,Field> fieldMap,String ... ignoreFieldNames){

  //需要忽略的對(duì)象字段
  List<String> ignoreNames = ReflectUtil.<String>resolveArrayToList(ignoreFieldNames);

  //移除忽略的對(duì)象字段
  fieldMap.keySet().removeAll(ignoreNames);
 }

 /**
  * 忽略掉非空的字段或者空的字段
  */
 public static void filterByFieldValue(Object object,Map<String,Field> fieldMap,boolean filterNullAble){

  Iterator<String> iterator = fieldMap.keySet().iterator();
  if(filterNullAble){
   while(iterator.hasNext()){
    try{
     //移除值為null的字段
     if(fieldMap.get(iterator.next()).get(object) == null){
      iterator.remove();
     }
    }catch(IllegalAccessException e){
     e.printStackTrace();
    }
   }
  }else {

   while(iterator.hasNext()){

    try{
     //移除字段不為null的字段
     if(fieldMap.get(iterator.next()).get(object) != null){
      iterator.remove();
     }
    }catch(IllegalAccessException e){
     e.printStackTrace();
    }
   }
  }
 }
}

補(bǔ)充知識(shí):Java將兩個(gè)JavaBean里相同的字段自動(dòng)填充

最近因?yàn)榻?jīng)常會(huì)操作講兩個(gè)JavaBean之間相同的字段互相填充,所以就寫了個(gè)偷懶的方法。記錄一下

/**
	 * 將兩個(gè)JavaBean里相同的字段自動(dòng)填充
	 * @param dto 參數(shù)對(duì)象
	 * @param obj 待填充的對(duì)象
	 */
	public static void autoFillEqFields(Object dto, Object obj) {
		try {
			Field[] pfields = dto.getClass().getDeclaredFields();
 
			Field[] ofields = obj.getClass().getDeclaredFields();
 
			for (Field of : ofields) {
				if (of.getName().equals("serialVersionUID")) {
					continue;
				}
				for (Field pf : pfields) {
					if (of.getName().equals(pf.getName())) {
						PropertyDescriptor rpd = new PropertyDescriptor(pf.getName(), dto.getClass());
						Method getMethod = rpd.getReadMethod();// 獲得讀方法
 
						PropertyDescriptor wpd = new PropertyDescriptor(pf.getName(), obj.getClass());
						Method setMethod = wpd.getWriteMethod();// 獲得寫方法
 
						setMethod.invoke(obj, getMethod.invoke(dto));
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
 
	/**
	 * 將兩個(gè)JavaBean里相同的字段自動(dòng)填充,按指定的字段填充
	 * @param dto
	 * @param obj
	 * @param String[] fields
	 */
	public static void autoFillEqFields(Object dto, Object obj, String[] fields) {
		try {
			Field[] ofields = obj.getClass().getDeclaredFields();
 
			for (Field of : ofields) {
				if (of.getName().equals("serialVersionUID")) {
					continue;
				}
				for (String field : fields) {
					if (of.getName().equals(field)) {
						PropertyDescriptor rpd = new PropertyDescriptor(field, dto.getClass());
						Method getMethod = rpd.getReadMethod();// 獲得讀方法
 
						PropertyDescriptor wpd = new PropertyDescriptor(field, obj.getClass());
						Method setMethod = wpd.getWriteMethod();// 獲得寫方法
 
						setMethod.invoke(obj, getMethod.invoke(dto));
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

但這樣寫不能把父類有的屬性自動(dòng)賦值所以修改了一下

/**
 * 將兩個(gè)JavaBean里相同的字段自動(dòng)填充
 * @param obj 原JavaBean對(duì)象
 * @param toObj 將要填充的對(duì)象
 */
 public static void autoFillEqFields(Object obj, Object toObj) {
 try {
 Map<String, Method> getMaps = new HashMap<>();
 Method[] sourceMethods = obj.getClass().getMethods();
 for (Method m : sourceMethods) {
 if (m.getName().startsWith("get")) {
  getMaps.put(m.getName(), m);
 }
 }
 
 Method[] targetMethods = toObj.getClass().getMethods();
 for (Method m : targetMethods) {
 if (!m.getName().startsWith("set")) {
  continue;
 }
 String key = "g" + m.getName().substring(1);
 Method getm = getMaps.get(key);
 if (null == getm) {
  continue;
 }
 // 寫入方法寫入
 m.invoke(toObj, getm.invoke(obj));
 }
 } catch (Exception e) {
 e.printStackTrace();
 }
 }

以上這篇利用Java反射機(jī)制實(shí)現(xiàn)對(duì)象相同字段的復(fù)制操作就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持億速云。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI