<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?

          共 15680字,需瀏覽 32分鐘

           ·

          2022-07-17 02:49

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

          前言

          在我們實(shí)際項(xiàng)目開發(fā)過程中,我們經(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ì)象格式又不一樣,所以我們需要編寫映射代碼將對(duì)象中的屬性值從一種類型轉(zhuǎn)換成另一種類型。

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

          對(duì)象拷貝

          在具體介紹兩種 BeanUtils之前,先來補(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ì)象的引用傳遞過去,他們實(shí)際還是指向的同一個(gè)對(duì)象。

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

          簡(jiǎn)單來說:

          • 淺拷貝 :對(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)容,此為深拷貝。

          BeanUtils

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

          apache 的 BeanUtils

          首先來看一個(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開發(fā)規(guī)約插件 上也明確指出:

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

          commons-beantutils 對(duì)于對(duì)象拷貝加了很多的檢驗(yàn),包括類型的轉(zhuǎn)換,甚至還會(huì)檢驗(yàn)對(duì)象所屬的類的可訪問性,可謂相當(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)行拷貝,只不過它的實(shí)現(xiàn)方式非常簡(jiǎn)單,就是對(duì)兩個(gè)對(duì)象中相同名字的屬性進(jìn)行簡(jiǎn)單的get/set,僅檢查屬性的可訪問性。具體實(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ì)跳過ignore的以及在源對(duì)象中不存在,所以這個(gè)方法是安全的,不會(huì)因?yàn)閮蓚€(gè)對(duì)象之間的結(jié)構(gòu)差異導(dǎo)致錯(cuò)誤,但是必須保證同名的兩個(gè)成員變量類型相同

          小結(jié)

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

          (感謝閱讀,希望對(duì)你所有幫助)
          來源:www.cnblogs.com/chen-chen-chen/p/12050985.html

          1、 19 張圖讓你秒懂 Spring Cloud 全家桶!

          2、 10 款牛哄哄的 Chrome 插件,你用了幾個(gè)?

          3、 在 IntelliJ IDEA 中這樣使用 Git,賊方便了!

          4、計(jì)算機(jī)時(shí)間到底是怎么來的?程序員必看的時(shí)間知識(shí)!

          5、這些IDEA的優(yōu)化設(shè)置趕緊安排起來,效率提升杠杠的!

          6、和100位00后聊完,我明白了為什么還有6億中國(guó)人仍不放棄QQ

          7、真香!用 IDEA 神器看源碼,效率真高!

          點(diǎn)分享 

          點(diǎn)收藏 

          點(diǎn)點(diǎn)贊 

          點(diǎn)在看

          瀏覽 46
          點(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>
                  欧美成人性爱视频 | 黄色成人网站在线观看免费 | 成天免费无码婬片在线观看免费 | ThePorn日本无码 | 人人看人人摸人人爱 |