<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          兩難!到底用 Spring BeanUtils 還是 Apache BeanUtils?

          共 15653字,需瀏覽 32分鐘

           ·

          2021-04-29 16:59

          前言

          在我們實(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ì)象中的屬性值從一種類(lèi)型轉(zhuǎn)換成另一種類(lèi)型。

          這種轉(zhuǎn)換最原始的方式就是手動(dòng)編寫(xiě)大量的 get/set代碼,當(dāng)然這是我們開(kāi)發(fā)過(guò)程不愿意去做的,因?yàn)樗_實(shí)顯得很繁瑣。為了解決這一痛點(diǎn),就誕生了一些方便的類(lèi)庫(kù),常用的有 apache的 BeanUtils,spring的 BeanUtilsDozer,Orika等拷貝工具。這篇文章主要介紹 Apache的BeanUtils 與 Spring 的BeanUtils,其他框架后續(xù)文章再做介紹

          對(duì)象拷貝

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

          什么是淺拷貝和深拷貝

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

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

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

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

          BeanUtils

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

          apache 的 BeanUtils

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

          public class PersonSource  {
              private Integer id;
              private String username;
              private String password;
              private Integer age;
              // getters/setters omiited
          }
          public class PersonDest {
              private Integer id;
              private String username;
              private Integer age;
              // getters/setters omiited
          }
          public class 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ì)象
          public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
              BeanUtilsBean.getInstance().copyProperties(dest, orig);
          }

          默認(rèn)情況下,使用org.apache.commons.beanutils.BeanUtils對(duì)復(fù)雜對(duì)象的復(fù)制是引用,這是一種淺拷貝

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

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

          commons-beantutils 對(duì)于對(duì)象拷貝加了很多的檢驗(yàn),包括類(lèi)型的轉(zhuǎn)換,甚至還會(huì)檢驗(yàn)對(duì)象所屬的類(lèi)的可訪(fǎng)問(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) {
                      throw new IllegalArgumentException
                              ("No destination bean specified");
                  }
                  if (orig == null) {
                      throw new 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);
                          }
                      }
                  } else if (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ì)象拷貝:

          public class 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,僅檢查屬性的可訪(fǎng)問(wèn)性。具體實(shí)現(xiàn)如下:

          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);
                }
               }
              }
             }
            }
           }

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

          小結(jié)

          以上簡(jiǎn)要的分析兩種BeanUtils,因?yàn)锳pache下的BeanUtils性能較差,不建議使用,可以使用 Spring的BeanUtils,或者使用其他拷貝框架,比如 cglib BeanCopier,基于javassist的Orika等,這些也是非常優(yōu)秀的類(lèi)庫(kù),值得去嘗試,并且也有人去評(píng)測(cè)過(guò)這些Bean映射工具,具體分析請(qǐng)參閱: 常見(jiàn)Bean映射工具分析評(píng)測(cè)及Orika介紹


          END



          免費(fèi)領(lǐng)取 1000+ 道面試資料!!小編這里有一份面試寶典《Java 核心知識(shí)點(diǎn).pdf》,覆蓋了 JVM,鎖、高并發(fā)、Spring原理、微服務(wù)、數(shù)據(jù)庫(kù)、Zookeep人、數(shù)據(jù)結(jié)構(gòu)等等知識(shí)點(diǎn),包含 Java 后端知識(shí)點(diǎn) 1000+ 個(gè),部分如下:

          如何獲取?加小編微信,回復(fù)【1024】

          瀏覽 79
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  插美女视频 | 天天干女人在线视频免费观看 | 一级无码电影 | 大香蕉国产在线 | 69视频网在线观看 |