1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        BeanUtils 是用 Spring 的還是 Apache 的好?

        共 7602字,需瀏覽 16分鐘

         ·

        2020-08-29 14:02



        來源 |?urlify.cn/vUfIry

        前言

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

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

        對象拷貝

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

        什么是淺拷貝和深拷貝

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

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

        簡單來說:

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

        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最常用的方法就是:

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

        默認情況下,使用org.apache.commons.beanutils.BeanUtils對復雜對象的復制是引用,這是一種淺拷貝

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

        Ali-Check | 避免用Apache Beanutils進行屬性的copy。

        commons-beantutils 對于對象拷貝加了很多的檢驗,包括類型的轉換,甚至還會檢驗對象所屬的類的可訪問性,可謂相當復雜,這也造就了它的差勁的性能,具體實現(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進行對象拷貝:

        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方法進行拷貝,只不過它的實現(xià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);
        ??????}
        ?????}
        ????}
        ???}
        ??}
        ?}

        可以看到,成員變量賦值是基于目標對象的成員列表,并且會跳過ignore的以及在源對象中不存在,所以這個方法是安全的,不會因為兩個對象之間的結構差異導致錯誤,但是必須保證同名的兩個成員變量類型相同

        小結

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

        你要“老婆”不?谷歌程序員20行代碼送你一個!

        不可不知的軟件架構模式

        Spring 的 Controller 是單例還是多例?怎么保證并發(fā)的安全

        瀏覽 52
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲精品一区无码A片丁香花 | 欧美老伦妇一区二区三区 | 二本道一区二区三区免费视频 | 黄色性视频 | 四虎影成人精品A片 | 91精品国产乱码香蕉黄瓜草莓 | 夜夜拍天天干 | 国产视频一区二区三区四 | 日本人日逼视频 | 日本亚洲中文字幕 |