溫馨提示×

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

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

Apache BeanUtils和Spring BeanUtils如何在Java項(xiàng)目中使用

發(fā)布時(shí)間:2020-11-16 15:11:52 來(lái)源:億速云 閱讀:128 作者:Leah 欄目:開(kāi)發(fā)技術(shù)

這篇文章給大家介紹Apache BeanUtils和Spring BeanUtils如何在Java項(xiàng)目中使用,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

# 前言

在我們實(shí)際項(xiàng)目開(kāi)發(fā)過(guò)程中,我們經(jīng)常需要將不同的兩個(gè)對(duì)象實(shí)例進(jìn)行屬性復(fù)制,從而基于源對(duì)象的屬性信息進(jìn)行后續(xù)操作,而不改變?cè)磳?duì)象的屬性信息,比如DTO數(shù)據(jù)傳輸對(duì)象和數(shù)據(jù)對(duì)象DO,我們需要將DO對(duì)象進(jìn)行屬性復(fù)制到DTO,但是對(duì)象格式又不一樣,所以我們需要編寫(xiě)映射代碼將對(duì)象中的屬性值從一種類型轉(zhuǎn)換成另一種類型。

# 對(duì)象拷貝

在具體介紹兩種 BeanUtils 之前,先來(lái)補(bǔ)充一些基礎(chǔ)知識(shí)。它們兩種工具本質(zhì)上就是對(duì)象拷貝工具,而對(duì)象拷貝又分為深拷貝和淺拷貝,下面進(jìn)行詳細(xì)解釋。

# 什么是淺拷貝和深拷貝

在Java中,除了 基本數(shù)據(jù)類型之外,還存在 類的實(shí)例對(duì)象這個(gè)引用數(shù)據(jù)類型,而一般使用 “=”號(hào)做賦值操作的時(shí)候,對(duì)于基本數(shù)據(jù)類型,實(shí)際上是拷貝的它的值,但是對(duì)于對(duì)象而言,其實(shí)賦值的只是這個(gè)對(duì)象的引用,將原對(duì)象的引用傳遞過(guò)去,他們實(shí)際還是指向的同一個(gè)對(duì)象。

而淺拷貝和深拷貝就是在這個(gè)基礎(chǔ)上做的區(qū)分,如果在拷貝這個(gè)對(duì)象的時(shí)候,只對(duì)基本數(shù)據(jù)類型進(jìn)行了拷貝,而對(duì)引用數(shù)據(jù)類型只是進(jìn)行引用的傳遞,而沒(méi)有真實(shí)的創(chuàng)建一個(gè)新的對(duì)象,則認(rèn)為是淺拷貝。反之,在對(duì)引用數(shù)據(jù)類型進(jìn)行拷貝的時(shí)候,創(chuàng)建了一個(gè)新的對(duì)象,并且復(fù)制其內(nèi)的成員變量,則認(rèn)為是深拷貝。

簡(jiǎn)單來(lái)說(shuō):

淺拷貝:對(duì)基本數(shù)據(jù)類型進(jìn)行值傳遞,對(duì)引用數(shù)據(jù)類型進(jìn)行引用傳遞般的拷貝,此為淺拷貝

深拷貝:對(duì)基本數(shù)據(jù)類型進(jìn)行值傳遞,對(duì)引用數(shù)據(jù)類型,創(chuàng)建一個(gè)新的對(duì)象,并復(fù)制其內(nèi)容,此為深拷貝。

Apache BeanUtils和Spring BeanUtils如何在Java項(xiàng)目中使用

# BeanUtils

前面簡(jiǎn)單講了一下對(duì)象拷貝的一些知識(shí),下面就來(lái)具體看下兩種 BeanUtils 工具

# Apache 的 BeanUtils

首先來(lái)看一個(gè)非常簡(jiǎn)單的BeanUtils的例子

publicclass PersonSource {
 private Integer id;
 private String username;
 private String password;
 private Integer age;
 // getters/setters omiited
}
publicclass PersonDest {
 private Integer id;
 private String username;
 private Integer age;
 // getters/setters omiited
}
publicclass TestApacheBeanUtils {
 public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
  //下面只是用于單獨(dú)測(cè)試
  PersonSource personSource = new PersonSource(1, "pjmike", "12345", 21);
  PersonDest personDest = new PersonDest();
  BeanUtils.copyProperties(personDest,personSource);
  System.out.println("persondest: "+personDest);
 }
}
persondest: PersonDest{id=1, username='pjmike', age=21}

從上面的例子可以看出,對(duì)象拷貝非常簡(jiǎn)單,BeanUtils最常用的方法就是:

//將源對(duì)象中的值拷貝到目標(biāo)對(duì)象//將源對(duì)象中的值拷貝到目標(biāo)對(duì)象
public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
 BeanUtilsBean.getInstance().copyProperties(dest, orig);
}

但是由于 Apache下的BeanUtils對(duì)象拷貝性能太差,不建議使用,而且在阿里巴巴Java開(kāi)發(fā)規(guī)約插件上也明確指出:

Ali-Check | 避免用Apache Beanutils進(jìn)行屬性的copy。

commons-beantutils 對(duì)于對(duì)象拷貝加了很多的檢驗(yàn),包括類型的轉(zhuǎn)換,甚至還會(huì)檢驗(yàn)對(duì)象所屬的類的可訪問(wèn)性,可謂相當(dāng)復(fù)雜,這也造就了它的差勁的性能,具體實(shí)現(xiàn)代碼如下:

public void copyProperties(final Object dest, final Object orig)
  throws IllegalAccessException, InvocationTargetException {

  // Validate existence of the specified beans
  if (dest == null) {
   thrownew IllegalArgumentException
     ("No destination bean specified");
  }
  if (orig == null) {
   thrownew IllegalArgumentException("No origin bean specified");
  }
  if (log.isDebugEnabled()) {
   log.debug("BeanUtils.copyProperties(" + dest + ", " +
      orig + ")");
  }

  // Copy the properties, converting as necessary
  if (orig instanceof DynaBean) {
   final DynaProperty[] origDescriptors =
    ((DynaBean) orig).getDynaClass().getDynaProperties();
   for (DynaProperty origDescriptor : origDescriptors) {
    final String name = origDescriptor.getName();
    // Need to check isReadable() for WrapDynaBean
    // (see Jira issue# BEANUTILS-61)
    if (getPropertyUtils().isReadable(orig, name) &&
     getPropertyUtils().isWriteable(dest, name)) {
     final Object value = ((DynaBean) orig).get(name);
     copyProperty(dest, name, value);
    }
   }
  } elseif (orig instanceof Map) {
   @SuppressWarnings("unchecked")
   final
   // Map properties are always of type <String, Object>
   Map<String, Object> propMap = (Map<String, Object>) orig;
   for (final Map.Entry<String, Object> entry : propMap.entrySet()) {
    final String name = entry.getKey();
    if (getPropertyUtils().isWriteable(dest, name)) {
     copyProperty(dest, name, entry.getValue());
    }
   }
  } else/* if (orig is a standard JavaBean) */ {
   final PropertyDescriptor[] origDescriptors =
    getPropertyUtils().getPropertyDescriptors(orig);
   for (PropertyDescriptor origDescriptor : origDescriptors) {
    final String name = origDescriptor.getName();
    if ("class".equals(name)) {
     continue; // No point in trying to set an object's class
    }
    if (getPropertyUtils().isReadable(orig, name) &&
     getPropertyUtils().isWriteable(dest, name)) {
     try {
      final Object value =
       getPropertyUtils().getSimpleProperty(orig, name);
      copyProperty(dest, name, value);
     } catch (final NoSuchMethodException e) {
      // Should not happen
     }
    }
   }
  }

 }

# Spring 的 BeanUtils

使用spring的BeanUtils進(jìn)行對(duì)象拷貝:

publicclass TestSpringBeanUtils {
 public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {

  //下面只是用于單獨(dú)測(cè)試
  PersonSource personSource = new PersonSource(1, "pjmike", "12345", 21);
  PersonDest personDest = new PersonDest();
  BeanUtils.copyProperties(personSource,personDest);
  System.out.println("persondest: "+personDest);
 }
}

Spring下的BeanUtils也是使用 copyProperties方法進(jìn)行拷貝,只不過(guò)它的實(shí)現(xiàn)方式非常簡(jiǎn)單,就是對(duì)兩個(gè)對(duì)象中相同名字的屬性進(jìn)行簡(jiǎn)單的get/set,僅檢查屬性的可訪問(wèn)性。具體實(shí)現(xiàn)如下:

private static void copyProperties(Object source, Object target, @Nullable Class<&#63;> editable,
  @Nullable String... ignoreProperties) throws BeansException {

 Assert.notNull(source, "Source must not be null");
 Assert.notNull(target, "Target must not be null");

 Class<&#63;> 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 &#63; 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);
   }
   }
  }
  }
 }
 }

可以看到,成員變量賦值是基于目標(biāo)對(duì)象的成員列表,并且會(huì)跳過(guò)ignore的以及在源對(duì)象中不存在,所以這個(gè)方法是安全的,不會(huì)因?yàn)閮蓚€(gè)對(duì)象之間的結(jié)構(gòu)差異導(dǎo)致錯(cuò)誤,但是必須保證同名的兩個(gè)成員變量類型相同

關(guān)于Apache BeanUtils和Spring BeanUtils如何在Java項(xiàng)目中使用就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向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