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

          BeanUtils 是用 Spring 的還是 Apache 的好?

          共 8085字,需瀏覽 17分鐘

           ·

          2020-08-19 23:22


          點擊上方“碼農(nóng)突圍”,馬上關(guān)注
          這里是碼農(nóng)充電第一站,回復(fù)“666”,獲取一份專屬大禮包
          真愛,請設(shè)置“星標(biāo)”或點個“在看”
          來源 |?urlify.cn/vUfIry

          前言

          在我們實際項目開發(fā)過程中,我們經(jīng)常需要將不同的兩個對象實例進(jìn)行屬性復(fù)制,從而基于源對象的屬性信息進(jìn)行后續(xù)操作,而不改變源對象的屬性信息,比如DTO數(shù)據(jù)傳輸對象和數(shù)據(jù)對象DO,我們需要將DO對象進(jìn)行屬性復(fù)制到DTO,但是對象格式又不一樣,所以我們需要編寫映射代碼將對象中的屬性值從一種類型轉(zhuǎn)換成另一種類型。
          這種轉(zhuǎn)換最原始的方式就是手動編寫大量的?get/set代碼,當(dāng)然這是我們開發(fā)過程不愿意去做的,因為它確實顯得很繁瑣。為了解決這一痛點,就誕生了一些方便的類庫,常用的有 apache的?BeanUtils,spring的?BeanUtils,?Dozer,Orika等拷貝工具。這篇文章主要介紹 Apache的BeanUtils 與 Spring 的BeanUtils,其他框架后續(xù)文章再做介紹

          對象拷貝

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

          什么是淺拷貝和深拷貝

          在Java中,除了?基本數(shù)據(jù)類型之外,還存在?類的實例對象這個引用數(shù)據(jù)類型,而一般使用 “=”號做賦值操作的時候,對于基本數(shù)據(jù)類型,實際上是拷貝的它的值,但是對于對象而言,其實賦值的只是這個對象的引用,將原對象的引用傳遞過去,他們實際還是指向的同一個對象。
          而淺拷貝和深拷貝就是在這個基礎(chǔ)上做的區(qū)分,如果在拷貝這個對象的時候,只對基本數(shù)據(jù)類型進(jìn)行了拷貝,而對引用數(shù)據(jù)類型只是進(jìn)行引用的傳遞,而沒有真實的創(chuàng)建一個新的對象,則認(rèn)為是淺拷貝。反之,在對引用數(shù)據(jù)類型進(jìn)行拷貝的時候,創(chuàng)建了一個新的對象,并且復(fù)制其內(nèi)的成員變量,則認(rèn)為是深拷貝。
          簡單來說:
          • 淺拷貝:對基本數(shù)據(jù)類型進(jìn)行值傳遞,對引用數(shù)據(jù)類型進(jìn)行引用傳遞般的拷貝,此為淺拷貝
          • 深拷貝:對基本數(shù)據(jù)類型進(jìn)行值傳遞,對引用數(shù)據(jù)類型,創(chuàng)建一個新的對象,并復(fù)制其內(nèi)容,此為深拷貝。

          BeanUtils

          前面簡單講了一下對象拷貝的一些知識,下面就來具體看下兩種BeanUtils工具

          apache 的 BeanUtils

          首先來看一個非常簡單的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?{
          ???????//下面只是用于單獨測試
          ????????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}
          從上面的例子可以看出,對象拷貝非常簡單,BeanUtils最常用的方法就是:
          //將源對象中的值拷貝到目標(biāo)對象
          public?static?void?copyProperties(Object?dest,?Object?orig)?throws?IllegalAccessException,?InvocationTargetException?{
          ????BeanUtilsBean.getInstance().copyProperties(dest,?orig);
          }
          默認(rèn)情況下,使用org.apache.commons.beanutils.BeanUtils對復(fù)雜對象的復(fù)制是引用,這是一種淺拷貝
          但是由于 Apache下的BeanUtils對象拷貝性能太差,不建議使用,而且在阿里巴巴Java開發(fā)規(guī)約插件上也明確指出:
          Ali-Check | 避免用Apache Beanutils進(jìn)行屬性的copy。
          commons-beantutils 對于對象拷貝加了很多的檢驗,包括類型的轉(zhuǎn)換,甚至還會檢驗對象所屬的類的可訪問性,可謂相當(dāng)復(fù)雜,這也造就了它的差勁的性能,具體實現(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?
          ????????????Map?propMap?=?(Map)?orig;
          ????????????for?(final?Map.Entry?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)行對象拷貝:
          public?class?TestSpringBeanUtils?{
          ????public?static?void?main(String[]?args)?throws?InvocationTargetException,?IllegalAccessException?{

          ???????//下面只是用于單獨測試
          ????????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)行拷貝,只不過它的實現(xiàn)方式非常簡單,就是對兩個對象中相同名字的屬性進(jìn)行簡單的get/set,僅檢查屬性的可訪問性。具體實現(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?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)對象的成員列表,并且會跳過ignore的以及在源對象中不存在,所以這個方法是安全的,不會因為兩個對象之間的結(jié)構(gòu)差異導(dǎo)致錯誤,但是必須保證同名的兩個成員變量類型相同

          小結(jié)

          以上簡要的分析兩種BeanUtils,因為Apache下的BeanUtils性能較差,不建議使用,可以使用 Spring的BeanUtils,或者使用其他拷貝框架,比如 cglib BeanCopier,基于javassist的Orika等,這些也是非常優(yōu)秀的類庫,值得去嘗試,并且也有人去評測過這些Bean映射工具,具體分析請參閱: 常見Bean映射工具分析評測及Orika介紹

          ---END---
          重磅!碼農(nóng)突圍-技術(shù)交流群已成立

          掃碼可添加碼農(nóng)突圍助手,可申請加入碼農(nóng)突圍大群和細(xì)分方向群,細(xì)分方向已涵蓋:Java、Python、機器學(xué)習(xí)、大數(shù)據(jù)、人工智能等群。
          一定要備注:開發(fā)方向+地點+學(xué)校/公司+昵稱(如Java開發(fā)+上海+拼夕夕+猴子),根據(jù)格式備注,可更快被通過且邀請進(jìn)群

          ▲長按加群

          推薦閱讀

          ? ?面試:如何決定使用 HashMap 還是 TreeMap?
          ???2020中國高校薪資排行出爐!好過雙一流的高校原來這么多!
          ???還只是 39 歲!一個天才年輕程序員的隕落
          ???認(rèn)真看看, 以后寫 SQL 就爽多了:MyBatis 動態(tài) SQL
          ?? 臥槽!微信居然有“隱身功能”了!
          ?? 滴滴十大技術(shù)方向開源項目出爐!有點牛?。?/a>

          最近面試BAT,整理一份面試資料Java面試BAT通關(guān)手冊,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點“在看”,關(guān)注公眾號并回復(fù)?BAT?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          如有收獲,點個在看,誠摯感謝明天見(??ω??)??

          瀏覽 30
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  日韩免费三级电影 | 99久久精品国产毛片 | wwwwww日本 | 91亚洲成人电影 | 欧美成人免费电影网站 |