常用的对象复制方法

  • Apache BeanUtils

  • Spring BeanUtils

  • Spring BeanCopier

  • Cglib BeanCopier

  • Hutool BeanUtil

  • get/set

测试环境

  • Java8

  • 4核16G台式机

依赖

<!-- Spring BeanUtils, Spring Copier -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.7.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- Apache BeanUtils -->
<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
</dependency>
<!-- Cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.5</version>
</dependency>

开始测试

定义两个实体类

package xyz.easyboot.demo;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * @author wujiawei0926@yeah.net
 * @see
 * @since 2020/5/15
 */
public class FromVo {

    public static FromVo create(){
        FromVo vo = new FromVo();
        vo.setDate(LocalDateTime.now());
        vo.setMoney(new BigDecimal(10000));
        vo.setId(9999);
        vo.setName("演示");
        return vo;
    }

    private String name;

    private Integer id;

    private LocalDateTime date;

    private BigDecimal money;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public LocalDateTime getDate() {
        return date;
    }

    public void setDate(LocalDateTime date) {
        this.date = date;
    }

    public BigDecimal getMoney() {
        return money;
    }

    public void setMoney(BigDecimal money) {
        this.money = money;
    }
}
package xyz.easyboot.demo;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * @author wujiawei0926@yeah.net
 * @see
 * @since 2020/5/15
 */
public class ToVo {

    public static ToVo create(){
        ToVo vo = new ToVo();
        vo.setDate(LocalDateTime.now());
        vo.setMoney(new BigDecimal(10000));
        vo.setId(9999);
        vo.setName("演示");
        return vo;
    }

    private String name;

    private Integer id;

    private LocalDateTime date;

    private BigDecimal money;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public LocalDateTime getDate() {
        return date;
    }

    public void setDate(LocalDateTime date) {
        this.date = date;
    }

    public BigDecimal getMoney() {
        return money;
    }

    public void setMoney(BigDecimal money) {
        this.money = money;
    }
}

Apache BeanUtils

package xyz.easyboot.demo;

import org.apache.commons.beanutils.BeanUtils;

import java.lang.reflect.InvocationTargetException;

/**
 * @author wujiawei0926@yeah.net
 * @see
 * @since 2020/5/15
 */
public class ApacheBeanUtilsDemo {

    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        for (int j = 0; j < 5; j++) {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 10000; i++) {
                FromVo from = FromVo.create();
                ToVo to = new ToVo();
                BeanUtils.copyProperties(to, from);
            }
            long end = System.currentTimeMillis();
            System.out.println("processing: " + (end - start));
        }

    }

}

Spring BeanUtils

package xyz.easyboot.demo;

import org.springframework.beans.BeanUtils;

/**
 * @author wujiawei0926@yeah.net
 * @see
 * @since 2020/5/15
 */
public class SpringBeanUtilsDemo {

    public static void main(String[] args) {
        for (int j = 0; j < 5; j++) {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 10000; i++) {
                FromVo from = FromVo.create();
                ToVo to = new ToVo();
                BeanUtils.copyProperties(from, to);
            }
            long end = System.currentTimeMillis();
            System.out.println("processing: " + (end - start));
        }
    }

}

Spring BeanCopier

package xyz.easyboot.demo;

import org.springframework.cglib.beans.BeanCopier;

/**
 * @author wujiawei0926@yeah.net
 * @see
 * @since 2020/5/15
 */
public class SpringBeanCopierDemo {

    public static void main(String[] args) {
        final BeanCopier copier = BeanCopier.create(FromVo.class, ToVo.class, false);
        for (int j = 0; j < 5; j++) {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 10000000; i++) {
                FromVo from = FromVo.create();
                ToVo to = new ToVo();
                copier.copy(from, to, null);
            }
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        }
    }

}

Cglib BeanCopier

package xyz.easyboot.demo;

import net.sf.cglib.beans.BeanCopier;

/**
 * @author wujiawei0926@yeah.net
 * @see
 * @since 2020/5/15
 */
public class CglibBeanCopierDemo {

    public static void main(String[] args) {
        final BeanCopier copier = BeanCopier.create(FromVo.class, ToVo.class, false);
        for (int j = 0; j < 5; j++) {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 10000000; i++) {
                FromVo from = FromVo.create();
                ToVo to = new ToVo();
                copier.copy(from, to, null);
            }
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        }
    }

}

Hutool BeanUtil

package xyz.easyboot.demo;

import cn.hutool.core.bean.BeanUtil;

/**
 * @author wujiawei0926@yeah.net
 * @see
 * @since 2020/5/21
 */
public class HutoolBeanUtilDemo {

    public static void main(String[] args) {
        for (int j = 0; j < 10000000; j++) {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 10; i++) {
                FromVo from = FromVo.create();
                ToVo to = new ToVo();
                BeanUtil.copyProperties(from, to);
            }
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        }
    }

}

Get/Set

package xyz.easyboot.demo;

/**
 * @author wujiawei0926@yeah.net
 * @see
 * @since 2020/5/15
 */
public class GetSetDemo {

    public static void main(String[] args) {
        for (int j = 0; j < 5; j++) {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 10000000; i++) {
                FromVo from = FromVo.create();
                ToVo to = new ToVo();
                to.setDate(from.getDate());
                to.setId(from.getId());
                to.setMoney(from.getMoney());
                to.setName(from.getName());
            }
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        }
    }

}

测试报告

测试报告

结论

可以看到跟网上的结论还是有些区别的,我的测试环境和依赖版本是导致区别的原因之一,当然也可能是我的测试方法不对。

  • 在次数很少的场景下更推荐Spring Beanutils

  • Spring BeanCopierCblib BeanCopier的性能相差无几

  • 次数非常多的场景中,get/setBeanCopier的速度差不多,但考虑到易用性,更推荐使用BeanCopier

  • Apache Beanutils无疑是最慢的,这点与网上的结论完全一致

  • 国产的Hutool BeanUtil在执行次数较少的场景下还是值得一试的,但测试10000000次时出现了明显的速度问题

源码地址

https://github.com/wuuJiawei/springboot-demos/tree/master/bean-copy-test/src/main/java/xyz/easyboot/demo

参考文献

你还在用BeanUtils进行对象属性拷贝?

Java Bean Copy组件的性能比较


本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

Mac升级到BigSur后Brew报错 上一篇
SpringBoot实现API限流(一) 下一篇