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

        共 1126字,需瀏覽 3分鐘

         ·

        2021-01-25 23:13

        點(diǎn)擊上方?好好學(xué)java?,選擇?星標(biāo)?公眾號(hào)

        重磅資訊,干貨,第一時(shí)間送達(dá)

        今日推薦:分享一套基于SpringBoot和Vue的企業(yè)級(jí)中后臺(tái)開(kāi)源項(xiàng)目,這個(gè)項(xiàng)目有點(diǎn)哇塞!

        個(gè)人原創(chuàng)100W +訪問(wèn)量博客:點(diǎn)擊前往,查看更多

        在日常開(kāi)發(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開(kāi)發(fā)手冊(cè)中提到禁止使用Apache BeanUtils呢?

        由于篇幅優(yōu)先,關(guān)于這幾種工具類的用法及區(qū)別,還有到底是什么是淺拷貝和深拷貝不在本文的討論范圍內(nèi)。

        本文主要聚焦于對(duì)比這幾個(gè)類庫(kù)的性能問(wèn)題。


        性能對(duì)比

        No Data No BB,我們就來(lái)寫(xiě)代碼來(lái)對(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)行測(cè)試類的編寫(xiě):

        使用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 BeanUtils
        5ms
        10ms
        45ms
        169ms
        Cglib BeanCopier
        4ms
        18ms
        45ms
        91ms
        Apache PropertyUtils
        60ms
        265ms
        1444ms
        11492ms
        Apache BeanUtils
        138ms
        816ms
        4154ms
        36938ms
        Dozer
        566ms
        2254ms
        11136ms
        102965ms

        畫(huà)了一張折線圖更方便大家進(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開(kāi)源出來(lái)的的類庫(kù)性能確不高呢?這不像是Apache的風(fēng)格呀,這背后導(dǎo)致性能低下的原因又是什么呢?
        其實(shí),是因?yàn)锳pache BeanUtils力求做得完美, 在代碼中增加了非常多的校驗(yàn)、兼容、日志打印等代碼,過(guò)度的包裝導(dǎo)致性能下降嚴(yán)重。


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

        原創(chuàng)電子書(shū)

        歷時(shí)整整一年總結(jié)的?Java面試+ Java入門技術(shù)學(xué)習(xí)指南,這是本人這幾年及校招的總結(jié),各種異步面試題已經(jīng)全部進(jìn)行總結(jié),按照章節(jié)復(fù)習(xí)即可,已經(jīng)拿到了了大廠提供。

        原創(chuàng)思維導(dǎo)圖

        掃碼或者微信搜?程序員的技術(shù)圈子?回復(fù)?面試?領(lǐng)取原創(chuàng)電子書(shū)和思維導(dǎo)圖。
        瀏覽 10
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 国产色在线 | 91精品国产日韩91久久久久久 | 扒开女同桌双腿嗯啊叫动漫 | 国产做19a爰片久久毛片A片 | 亚洲永久色 |