1. 為什么阿里巴巴禁止使用Apache Beanutils進(jìn)行屬性的copy?

        共 1105字,需瀏覽 3分鐘

         ·

        2020-07-30 17:44

        △Hollis, 一個(gè)對(duì)Coding有著獨(dú)特追求的人△
        這是Hollis的第?297?篇原創(chuàng)分享
        作者 l Hollis
        來源 l Hollis(ID:hollischuang)
        在日常開發(fā)中,我們經(jīng)常需要給對(duì)象進(jìn)行賦值,通常會(huì)調(diào)用其set/get方法,有些時(shí)候,如果我們要轉(zhuǎn)換的兩個(gè)對(duì)象之間屬性大致相同,會(huì)考慮使用屬性拷貝工具進(jìn)行。
        如我們經(jīng)常在代碼中會(huì)對(duì)一個(gè)數(shù)據(jù)結(jié)構(gòu)封裝成DO、SDO、DTO、VO等,而這些Bean中的大部分屬性都是一樣的,所以使用屬性拷貝類工具可以幫助我們節(jié)省大量的set和get操作。
        市面上有很多類似的工具類,比較常用的有
        1、Spring BeanUtils?
        2、Cglib BeanCopier?
        3、Apache BeanUtils?
        4、Apache PropertyUtils?
        5、Dozer
        那么,我們到底應(yīng)該選擇哪種工具類更加合適呢?為什么阿里巴巴Java開發(fā)手冊中提到禁止使用Apache BeanUtils呢?
        ?由于篇幅優(yōu)先,關(guān)于這幾種工具類的用法及區(qū)別,還有到底是什么是淺拷貝和深拷貝不在本文的討論范圍內(nèi)。
        本文主要聚焦于對(duì)比這幾個(gè)類庫的性能問題。



        性能對(duì)比
        No Data No BB,我們就來寫代碼來對(duì)比下這幾種框架的性能情況。
        代碼示例如下:
        首先定義一個(gè)PersonDO類:
        public?class?PersonDO?{

        ????private?Integer?id;

        ????private?String?name;

        ????private?Integer?age;

        ????private?Date?birthday;

        ????//省略setter/getter

        }
        再定義一個(gè)PersonDTO類:
        public?class?PersonDTO?{

        ????private?String?name;

        ????private?Integer?age;

        ????private?Date?birthday;

        }
        然后進(jìn)行測試類的編寫:
        使用Spring BeanUtils進(jìn)行屬性拷貝:

        private?void?mappingBySpringBeanUtils(PersonDO?personDO,?int?times)?{

        ????StopWatch?stopwatch?=?new?StopWatch();

        ????stopwatch.start();


        ????for?(int?i?=?0;?i?
        ????????PersonDTO?personDTO?=?new?PersonDTO();

        ????????org.springframework.beans.BeanUtils.copyProperties(personDO,?personDTO);

        ????}

        ????stopwatch.stop();

        ????System.out.println("mappingBySpringBeanUtils?cost?:"?+?stopwatch.getTotalTimeMillis());

        }

        其中的StopWatch用于記錄代碼執(zhí)行時(shí)間,方便進(jìn)行對(duì)比。
        使用Cglib BeanCopier進(jìn)行屬性拷貝:

        private?void?mappingByCglibBeanCopier(PersonDO?personDO,?int?times)?{

        ????StopWatch?stopwatch?=?new?StopWatch();

        ????stopwatch.start();

        ????for?(int?i?=?0;?i?
        ????????PersonDTO?personDTO?=?new?PersonDTO();

        ????????BeanCopier?copier?=?BeanCopier.create(PersonDO.class,?PersonDTO.class,?false);

        ????????copier.copy(personDO,?personDTO,?null);

        ????}

        ????stopwatch.stop();

        ????System.out.println("mappingByCglibBeanCopier?cost?:"?+?stopwatch.getTotalTimeMillis());

        }

        使用Apache BeanUtils進(jìn)行屬性拷貝:

        private?void?mappingByApacheBeanUtils(PersonDO?personDO,?int?times)

        ????throws?InvocationTargetException,?IllegalAccessException?
        {

        ????StopWatch?stopwatch?=?new?StopWatch();

        ????stopwatch.start();

        ????for?(int?i?=?0;?i?
        ????????PersonDTO?personDTO?=?new?PersonDTO();

        ????????BeanUtils.copyProperties(personDTO,?personDO);

        ????}

        ????stopwatch.stop();

        ????System.out.println("mappingByApacheBeanUtils?cost?:"?+?stopwatch.getTotalTimeMillis());

        }

        使用Apache PropertyUtils進(jìn)行屬性拷貝:

        private?void?mappingByApachePropertyUtils(PersonDO?personDO,?int?times)

        ????throws?InvocationTargetException,?IllegalAccessException,?NoSuchMethodException?
        {

        ????StopWatch?stopwatch?=?new?StopWatch();

        ????stopwatch.start();

        ????for?(int?i?=?0;?i?
        ????????PersonDTO?personDTO?=?new?PersonDTO();

        ????????PropertyUtils.copyProperties(personDTO,?personDO);

        ????}

        ????stopwatch.stop();

        ????System.out.println("mappingByApachePropertyUtils?cost?:"?+?stopwatch.getTotalTimeMillis());

        }

        然后執(zhí)行以下代碼:

        public?static?void?main(String[]?args)

        ????throws?InvocationTargetException,?IllegalAccessException,?NoSuchMethodException?
        {

        ????PersonDO?personDO?=?new?PersonDO();

        ????personDO.setName("Hollis");

        ????personDO.setAge(26);

        ????personDO.setBirthday(new?Date());

        ????personDO.setId(1);


        ????MapperTest?mapperTest?=?new?MapperTest();


        ????mapperTest.mappingBySpringBeanUtils(personDO,?100);

        ????mapperTest.mappingBySpringBeanUtils(personDO,?1000);

        ????mapperTest.mappingBySpringBeanUtils(personDO,?10000);

        ????mapperTest.mappingBySpringBeanUtils(personDO,?100000);

        ????mapperTest.mappingBySpringBeanUtils(personDO,?1000000);

        ????mapperTest.mappingByCglibBeanCopier(personDO,?100);

        ????mapperTest.mappingByCglibBeanCopier(personDO,?1000);

        ????mapperTest.mappingByCglibBeanCopier(personDO,?10000);

        ????mapperTest.mappingByCglibBeanCopier(personDO,?100000);

        ????mapperTest.mappingByCglibBeanCopier(personDO,?1000000);

        ????mapperTest.mappingByApachePropertyUtils(personDO,?100);

        ????mapperTest.mappingByApachePropertyUtils(personDO,?1000);

        ????mapperTest.mappingByApachePropertyUtils(personDO,?10000);

        ????mapperTest.mappingByApachePropertyUtils(personDO,?100000);

        ????mapperTest.mappingByApachePropertyUtils(personDO,?1000000);

        ????mapperTest.mappingByApacheBeanUtils(personDO,?100);

        ????mapperTest.mappingByApacheBeanUtils(personDO,?1000);

        ????mapperTest.mappingByApacheBeanUtils(personDO,?10000);

        ????mapperTest.mappingByApacheBeanUtils(personDO,?100000);

        ????mapperTest.mappingByApacheBeanUtils(personDO,?1000000);

        }

        得到結(jié)果如下:
        工具類執(zhí)行1000次耗時(shí)執(zhí)行10000次耗時(shí)執(zhí)行100000次耗時(shí)執(zhí)行1000000次耗時(shí)
        Spring BeanUtils5ms10ms45ms169ms
        Cglib BeanCopier4ms18ms45ms91ms
        Apache PropertyUtils60ms265ms1444ms11492ms
        Apache BeanUtils138ms816ms4154ms36938ms
        Dozer566ms2254ms11136ms102965ms

        畫了一張折線圖更方便大家進(jìn)行對(duì)比

        綜上,我們基本可以得出結(jié)論,在性能方面,Spring BeanUtils和Cglib BeanCopier表現(xiàn)比較不錯(cuò),而Apache PropertyUtils、Apache BeanUtils以及Dozer則表現(xiàn)的很不好。
        所以,如果考慮性能情況的話,建議大家不要選擇Apache PropertyUtils、Apache BeanUtils以及Dozer等工具類。
        很多人會(huì)不理解,為什么大名鼎鼎的Apache開源出來的的類庫性能確不高呢?這不像是Apache的風(fēng)格呀,這背后導(dǎo)致性能低下的原因又是什么呢?
        其實(shí),是因?yàn)锳pache BeanUtils力求做得完美, 在代碼中增加了非常多的校驗(yàn)、兼容、日志打印等代碼,過度的包裝導(dǎo)致性能下降嚴(yán)重。



        總結(jié)
        本文通過對(duì)比幾種常見的屬性拷貝的類庫,分析得出了這些工具類的性能情況,最終也驗(yàn)證了《阿里巴巴Java開發(fā)手冊》中提到的"Apache BeanUtils 效率低"的事實(shí)。
        但是本文只是站在性能這一單一角度進(jìn)行了對(duì)比,我們在選擇一個(gè)工具類的時(shí)候還會(huì)有其他方面的考慮,比如使用成本、理解難度、兼容性、可擴(kuò)展性等,對(duì)于這種拷貝類工具類,我們還會(huì)考慮其功能是否完善等。
        就像雖然Dozer性能比較差,但是他可以很好的和Spring結(jié)合,可以通過配置文件等進(jìn)行屬性之間的映射等,也受到了很多開發(fā)者的喜愛。
        本文用到的第三方類庫的maven依賴如下:



        <dependency>

        ????<groupId>commons-beanutilsgroupId>


        ????<artifactId>commons-beanutilsartifactId>

        ????<version>1.9.4version>

        dependency>



        <dependency>

        ????<groupId>commons-logginggroupId>

        ????<artifactId>commons-loggingartifactId>

        ????<version>1.1.2version>

        dependency>





        <dependency>

        ????<groupId>org.springframeworkgroupId>

        ????<artifactId>org.springframework.beansartifactId>

        ????<version>3.1.1.RELEASEversion>

        dependency>





        <dependency>

        ????<groupId>cglibgroupId>

        ????<artifactId>cglib-nodepartifactId>

        ????<version>2.2.2version>

        dependency>





        <dependency>

        ????<groupId>net.sf.dozergroupId>

        ????<artifactId>dozerartifactId>

        ????<version>5.5.1version>

        dependency>





        <dependency>

        ????<groupId>org.slf4jgroupId>

        ????<artifactId>slf4j-apiartifactId>

        ????<version>1.7.7version>

        dependency>



        <dependency>

        ????<groupId>org.slf4jgroupId>

        ????<artifactId>jul-to-slf4jartifactId>

        ????<version>1.7.7version>

        dependency>



        <dependency>

        ????<groupId>org.slf4jgroupId>

        ????<artifactId>jcl-over-slf4jartifactId>

        ????<version>1.7.7version>

        dependency>



        <dependency>

        ????<groupId>org.slf4jgroupId>

        ????<artifactId>log4j-over-slf4jartifactId>

        ????<version>1.7.7version>

        dependency>



        <dependency>

        ????<groupId>org.slf4jgroupId>

        ????<artifactId>slf4j-jdk14artifactId>

        ????<version>1.7.7version>

        dependency>



        關(guān)于作者Hollis(ID:hollischuang),一個(gè)對(duì)Coding有著獨(dú)特追求的人,現(xiàn)任阿里巴巴技術(shù)專家,個(gè)人技術(shù)博主,技術(shù)文章全網(wǎng)閱讀量數(shù)千萬,《程序員的三門課》聯(lián)合作者。


        往期推薦

        阿里的簡歷多久可以投遞一次?次數(shù)多了有沒有影響?可以同時(shí)進(jìn)行嗎?


        什么是a站、b站、c站、d站、e站、f站、g站、h站、i站、j站、k站、l站、m站、n站…z站?


        一口氣說出 4 種分布式一致性 Session 實(shí)現(xiàn)方式,面試杠杠的~


        ?

        直面Java第329期:哪個(gè)命令可以監(jiān)控虛擬機(jī)各種運(yùn)行狀態(tài)信息?

        深入并發(fā)第013期:拓展synchronized——鎖優(yōu)化


        如果你喜歡本文,

        請長按二維碼,關(guān)注?Hollis.

        轉(zhuǎn)發(fā)至朋友圈,是對(duì)我最大的支持。


        點(diǎn)個(gè)?在看?
        喜歡是一種感覺
        在看是一種支持
        ↘↘↘
        瀏覽 22
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 欧美在线无码视频 | 性少妇jealousvue成熟 | 电影《囗交》未删减版 | 亚洲天堂网一区二区三区 | 我要看黄色录像一级片 |