几种copyProperties工具类性能比较

一、事出有因

前段时间阿里发布了阿里巴巴代码规约插件,果断将它集成起来。右键->阿里编码规约扫描,立即将不符合阿里编程规范的代码现了原形,不得不服阿里想统一整个java市场的决心啊。怎么?竟然看到我最喜欢使用的Apache BeanUtils.copyProperties()方法后面打了个大大的红叉,提示"避免使用Apache的BeanUtils进行属性的copy"。心里确实不是滋味,从小老师就教导我们,"凡是Apache写的框架都是好框架",怎么可能会存在"性能问题"--还是这种猿们所不能容忍的问题。心存着对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执行" + 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 耗时: " + 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执行" + len + "次================");
        DestinationObject destination2 = new DestinationObject();
        for (int i = 0; i < len; i++) {
            BeanUtils.copyProperties(destination2, origin);
        }

        stopwatch.stop();

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

    private static void testSpringFramework(OriginObject origin, int len) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        System.out.println("================springframework执行" + 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 耗时: " + 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执行" + len + "次================");
        DestinationObject destination2 = new DestinationObject();
        for (int i = 0; i < len; i++) {
            PropertyUtils.copyProperties(destination2, origin);
        }

        stopwatch.stop();

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

分别执行1000、10000、100000、1000000次耗时数(毫秒):

工具名称 执行1000次耗时 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
结论:
  1. Apache BeanUtils的性能最差,不建议使用。
  2. Apache PropertyUtils100000次以内性能还能接受,到百万级别性能就比较差了,可酌情考虑。
  3. spring BeanUtils和BeanCopier性能较好,如果对性能有特别要求,可使用BeanCopier,不然spring BeanUtils也是可取的。
四、Apache BeanUtils.copyProperties()性能分析

执行1000000次copy属性,然后通过jvisualvm查看方法耗时,如图:


几种copyProperties工具类性能比较_第1张图片
最耗时方法.png

发现最耗时的方法就是method.invoke(),但是spring的BeanUtils、PropertyUtils里也是采用反射来实现的,为什么效率相差这么大呢?

几种copyProperties工具类性能比较_第2张图片
Paste_Image.png

看来Abstract.convert()和getIntrospectionData()占用了很大一部分时间.而且Apache BeanUtils中的日志输出也比较耗时。

五、看看Spring BeanUtils和PropertyUtils
几种copyProperties工具类性能比较_第3张图片
Apache BeanUtils和PropertyUtils对比.png

PropertyUtils和Apache BeanUtils核心代码区别在图中标注的地方。Apache BeanUtils主要集中了各种丰富的功能(日志、转换、解析等等),导致性能变差。

而Spring BeanUtils则是直接通过反射来读取和写入,直抒胸臆,省去了其他繁杂的步骤,性能自然不差。

几种copyProperties工具类性能比较_第4张图片
Paste_Image.png
六、Cglib BeanCopier

cglib BeanCopier的主要耗时方法就在BeanCopier.create(),如果将该方法做成静态成员变量,则还可以大大缩小执行时间。BeanCopier是一种基于字节码的方式,其实就是通过字节码方式转换成性能最好的get、set方式,只需考虑创建BeanCopier的开销,如果我们将BeanCopier做成静态的,基本只需考虑get、set的开销,所以性能接近于get、set。BeanCopier源码分析可见:http://www.jianshu.com/p/f8b892e08d26,我这里就不展开了。

你可能感兴趣的:(几种copyProperties工具类性能比较)