幾種copyProperties工具類性能比較

一、事出有因

前段時(shí)間阿里發(fā)布了阿里巴巴代碼規(guī)約插件,果斷將它集成起來。右鍵->阿里編碼規(guī)約掃描,立即將不符合阿里編程規(guī)范的代碼現(xiàn)了原形,不得不服阿里想統(tǒng)一整個(gè)java市場的決心啊。怎么?竟然看到我最喜歡使用的Apache BeanUtils.copyProperties()方法后面打了個(gè)大大的紅叉,提示"避免使用Apache的BeanUtils進(jìn)行屬性的copy"。心里確實(shí)不是滋味,從小老師就教導(dǎo)我們,"凡是Apache寫的框架都是好框架",怎么可能會(huì)存在"性能問題"--還是這種猿們所不能容忍的問題。心存著對(duì)BeanUtils的懷疑開始了今天的研究之路。

二、市面上的其他幾種屬性copy工具
  1. springframework的BeanUtils
  2. cglib的BeanCopier
  3. Apache BeanUtils包的PropertyUtils類
三、下面來測試一下性能。
private static void testCglibBeanCopier(OriginObject origin, int len) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        System.out.println();
        System.out.println("================cglib BeanCopier執(zhí)行" + len + "次================");
        DestinationObject destination3 = new DestinationObject();

        for (int i = 0; i < len; i++) {
            BeanCopier copier = BeanCopier.create(OriginObject.class, DestinationObject.class, false);
            copier.copy(origin, destination3, null);
        }
        stopwatch.stop();

        System.out.println("testCglibBeanCopier 耗時(shí): " + stopwatch.elapsed(TimeUnit.MILLISECONDS));
    }

    private static void testApacheBeanUtils(OriginObject origin, int len)
            throws IllegalAccessException, InvocationTargetException {
        Stopwatch stopwatch = Stopwatch.createStarted();
        System.out.println();
        System.out.println("================apache BeanUtils執(zhí)行" + len + "次================");
        DestinationObject destination2 = new DestinationObject();
        for (int i = 0; i < len; i++) {
            BeanUtils.copyProperties(destination2, origin);
        }

        stopwatch.stop();

        System.out.println("testApacheBeanUtils 耗時(shí): " + stopwatch.elapsed(TimeUnit.MILLISECONDS));
    }

    private static void testSpringFramework(OriginObject origin, int len) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        System.out.println("================springframework執(zhí)行" + len + "次================");
        DestinationObject destination = new DestinationObject();

        for (int i = 0; i < len; i++) {
            org.springframework.beans.BeanUtils.copyProperties(origin, destination);
        }

        stopwatch.stop();

        System.out.println("testSpringFramework 耗時(shí): " + stopwatch.elapsed(TimeUnit.MILLISECONDS));
    }

    private static void testApacheBeanUtilsPropertyUtils(OriginObject origin, int len)
            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        Stopwatch stopwatch = Stopwatch.createStarted();
        System.out.println();
        System.out.println("================apache BeanUtils PropertyUtils執(zhí)行" + len + "次================");
        DestinationObject destination2 = new DestinationObject();
        for (int i = 0; i < len; i++) {
            PropertyUtils.copyProperties(destination2, origin);
        }

        stopwatch.stop();

        System.out.println("testApacheBeanUtilsPropertyUtils 耗時(shí): " + stopwatch.elapsed(TimeUnit.MILLISECONDS));
    }

分別執(zhí)行1000、10000、100000、1000000次耗時(shí)數(shù)(毫秒):

工具名稱 執(zhí)行1000次耗時(shí) 10000次 100000次 1000000次
Apache BeanUtils 390ms 854ms 1763ms 8408ms
Apache PropertyUtils 26ms 221ms 352ms 2663ms
spring BeanUtils 39ms 315ms 373ms 949ms
Cglib BeanCopier 64ms 144ms 171ms 309ms
結(jié)論:
  1. Apache BeanUtils的性能最差,不建議使用。
  2. Apache PropertyUtils100000次以內(nèi)性能還能接受,到百萬級(jí)別性能就比較差了,可酌情考慮。
  3. spring BeanUtils和BeanCopier性能較好,如果對(duì)性能有特別要求,可使用BeanCopier,不然spring BeanUtils也是可取的。
四、Apache BeanUtils.copyProperties()性能分析

執(zhí)行1000000次copy屬性,然后通過jvisualvm查看方法耗時(shí),如圖:


最耗時(shí)方法.png

發(fā)現(xiàn)最耗時(shí)的方法就是method.invoke(),但是spring的BeanUtils、PropertyUtils里也是采用反射來實(shí)現(xiàn)的,為什么效率相差這么大呢?

Paste_Image.png

看來Abstract.convert()和getIntrospectionData()占用了很大一部分時(shí)間.而且Apache BeanUtils中的日志輸出也比較耗時(shí)。

五、看看Spring BeanUtils和PropertyUtils
Apache BeanUtils和PropertyUtils對(duì)比.png

PropertyUtils和Apache BeanUtils核心代碼區(qū)別在圖中標(biāo)注的地方。Apache BeanUtils主要集中了各種豐富的功能(日志、轉(zhuǎn)換、解析等等),導(dǎo)致性能變差。

而Spring BeanUtils則是直接通過反射來讀取和寫入,直抒胸臆,省去了其他繁雜的步驟,性能自然不差。

Paste_Image.png
六、Cglib BeanCopier

cglib BeanCopier的主要耗時(shí)方法就在BeanCopier.create(),如果將該方法做成靜態(tài)成員變量,則還可以大大縮小執(zhí)行時(shí)間。BeanCopier是一種基于字節(jié)碼的方式,其實(shí)就是通過字節(jié)碼方式轉(zhuǎn)換成性能最好的get、set方式,只需考慮創(chuàng)建BeanCopier的開銷,如果我們將BeanCopier做成靜態(tài)的,基本只需考慮get、set的開銷,所以性能接近于get、set。BeanCopier源碼分析可見:http://www.itdecent.cn/p/f8b892e08d26,我這里就不展開了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容