查看原文
其他

Java Bean Copy性能对比

easyboot SpringForAll社区 2021-05-27

点击上方☝SpringForAll社区 轻松关注!

及时获取有趣有料的技术文章

本文来源:http://rrd.me/g6P3V


常用的对象复制方法

  • 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>Copy
<!-- Apache BeanUtils -->
<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
</dependency>Copy
<!-- Cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>Copy
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.5</version>
</dependency>Copy

开始测试

定义两个实体类

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;
    }
}Copy
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;
    }
}Copy

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));
        }

    }

}Copy

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));
        }
    }

}Copy

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);
        }
    }

}Copy

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);
        }
    }

}Copy

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);
        }
    }

}Copy

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);
        }
    }

}Copy

测试报告

结论

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

  • 在次数很少的场景下更推荐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组件的性能比较


墙裂推荐

【深度】互联网技术人的社群,点击了解!



● Java后端开发实践-项目模板

● 提高代码质量的Tips

● 基于SpringBoot为微服务架构编写端到端测试

● 理解分布式系统的8个谬误


关注公众号,回复“spring”有惊喜!!!

如果资源对你有帮助的话

❤️给个「在看」,是最大的支持❤️

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存