主流属性拷贝工具速度比较

1 篇文章 0 订阅
摘要

本文介绍四种主流的属性拷贝工具:

  1. PropertyUtils (commons beanutils)
  2. BeanUtils (commons beanutils)
  3. BeanUtils (Spring beans)
  4. BeanCopier (cglib)

它们的属性拷贝都基于java内省,拷贝的对象类型必须提供setter/getter;

经过拷贝速度测试:

  1. getter/setter 速度最快,但是不适用于数量多的属性拷贝;
  2. BeanCopier 速度位居第二,在使用缓存的情况下,甚至与getter/setter相差无几;
  3. Spring BeanUtils 在次数增加的情况下,速度较好,在次数少时,比commons beanutils的两种工具都要慢;
  4. PropertyUtils 比BeanUtils 速度快,更稳定。
一、属性拷贝

属性拷贝

日常开发中,经常会有将一个java bean的属性拷贝到另一个java bean的需求。这两个bean可能是相同类型;也可能是不同类型,但是有部分相同字段。

最直观的做法是使用setter/getter:

objA.setName(objB.getName());

这种方法效率是最高的;但是当要拷贝的属性较多时,setter/getter就不方便,不优雅了。

有需求就会有相应的解决方案,以下四种主流工具提供了属性拷贝功能:

  1. org.apache.commons.beanutils.PropertyUtils#copyProperties()
  2. org.apache.commons.beanutils.BeanUtils#copyProperties()
  3. org.springframework.beans.BeanUtils#copyProperties()
  4. net.sf.cglib.beans.BeanCopier

依赖

org.apache.commons.beanutils.PropertyUtils.copyProperties以及org.apache.commons.beanutils.BeanUtils.copyProperties都由commons-beanutils提供:

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.3</version>
</dependency>

org.springframework.beans.BeanUtils.copyProperties由spring-beans提供:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.1.3.RELEASE</version>
</dependency>

net.sf.cglib.beans.BeanCopier由cglib提供:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.7</version>
</dependency>

使用方法

属性拷贝工具的使用方法都比较简单,除了BeanCopier外,其他三个类都提供了静态方法直接调用。

cglib的net.sf.cglib.beans.BeanCopier稍微麻烦一点,需要先创建copier:

BeanCopier beanCopier = net.sf.cglib.beans.BeanCopier.create(SourceBean.class, TargetBean.class, false);
beanCopier.copy(from, to, null);

四种工具的属性拷贝原理,都是利用了java内省机制;所以,需要拷贝的java bean类型都必须提供getter以及setter方法,否者拷贝失败。

同名不同类型

拷贝时,遇到同名属性,但是类型不同,几种工具表现也不同:

org.apache.commons.beanutils.PropertyUtils#copyProperties() (同名setter,类型不同,异常)
org.apache.commons.beanutils.BeanUtils#copyProperties() (同名setter,即使类型不同也能转换,但是值可能不正确)
org.springframework.beans.BeanUtils#copyProperties() (同名setter,即使类型不同也能转换,但是值可能不正确)
net.sf.cglib.beans.BeanCopier(同名setter,即使类型不同也能转换,但是值可能不正确)

二、速度比较

经过代码测试, 10次,1000次,1000000次各种手段的速度表现如下:

10次测试第一次第二次第三次平均值每次平均值
PropertyUtils.copyProperties0101/30.033
BeanUtils.copyProperties2322.330.233
SPRING copyProperties00000
BeanCopier(without cache)00000
BeanCopier00000
getter/setter00000
1000次测试第一次第二次第三次平均值每次平均值
PropertyUtils.copyProperties22211619.660.01966
BeanUtils.copyProperties19292022.660.02266
SPRING copyProperties7555.660.00566
BeanCopier(without cache)4433.660.00366
BeanCopier0010.330.00033
getter/setter00000
1000000次测试第一次第二次第三次平均值每次平均值
PropertyUtils.copyProperties1798177018041767.330.00176733
BeanUtils.copyProperties2904293728602900.330.00290033
SPRING copyProperties214206199206.330.00020633
BeanCopier(without cache)92828987.660.00008766
BeanCopier8787.660.00000766
getter/setter8777.330.00000733

可以得出结论:

  1. getter/setter 速度最快,但是不适用于数量多的属性拷贝;
  2. BeanCopier 速度位居第二,在使用缓存的情况下,甚至与getter/setter相差无几;即使不使用缓存,速度也很快;
  3. Spring BeanUtils 在次数增加的情况下,速度较好,在次数少时,比commons beanutils的两种工具都要慢;
  4. PropertyUtils 比BeanUtils 速度快,更稳定。

需要注意一点,BeanCopier#create开销比较大,所以不要频繁创建beanCopier实体,最使用缓存。

测试速度的代码:

public class BeanCopyCompareTest {

    private static BeanCopier beanCopier = net.sf.cglib.beans.BeanCopier.create(SourceBean.class, TargetBean.class, false);

    public static void main(String[] args) throws Exception {

        int[] times = {1, 10, 1000, 1000000};

        for (int time : times) {
            doCopy(time, (from, to) -> org.apache.commons.beanutils.PropertyUtils.copyProperties(to, from),
                    "org.apache.commons.beanutils.PropertyUtils.copyProperties");
            doCopy(time, (from, to) -> org.apache.commons.beanutils.BeanUtils.copyProperties(to, from),
                    "org.apache.commons.beanutils.BeanUtils.copyProperties");
            doCopy(time, (BeanUtils::copyProperties),
                    "org.springframework.beans.BeanUtils.copyProperties");
            doCopy(time, ((from, to) -> beanCopier.copy(from, to, null)), "net.sf.cglib.beans.BeanCopier");
            doCopy(time, ((from, to) -> {
                BeanCopier beanCopier = net.sf.cglib.beans.BeanCopier.create(SourceBean.class, TargetBean.class, false);
                beanCopier.copy(from, to, null);
            }), "net.sf.cglib.beans.BeanCopier(without cache)");
            doCopy(time, ((from, to) -> {
                to.setAge(from.getAge());
                to.setGender(from.getGender());
                to.setName(from.getName());
                to.setRight(from.getRight());
            }), "getter/setter");

            System.out.println("-----------------------------------------------------");
        }

    }

    static void doCopy(Integer time, BeanCopy beanCopy, String type) throws Exception {
        long startTime = System.currentTimeMillis();
        for (int j = 0; j < time; j++) {
            SourceBean sourceBean = new SourceBean();
            sourceBean.setName("Richard");
            sourceBean.setGender('F');
            sourceBean.setAge(20);
            sourceBean.setRight(true);

            TargetBean targetBean = new TargetBean();
            beanCopy.copy(sourceBean, targetBean);
        }
        System.out.printf("执行%d次用时%dms---------%s%n", time, System.currentTimeMillis() - startTime, type);
    }

    interface BeanCopy {
        void copy(SourceBean from, TargetBean to) throws Exception;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值