浅谈常用的复制JavaBean的工具类

Reading time ~43 minutes

此文章为原创文章,引用请标记文章出处: 浅谈常用的复制JavaBean的工具类

概述

两个相似的javaBean之间的复制,简单的可以使用get/set方法实现复制,其效率也是最高的,但过于繁琐,所以本文探讨一些比较流行的实现javaBean对象间复制的开源插件。

工具类介绍

开源的工具类:DozerMapper、Apache BeanUtils和 Apache PropertyUtils、Spring BeanUtils、Jodd BeanUtils、Cglib

以下简单介绍开源工具:

DozerMapper

XML/API/Annotation的方式,支持简单形式的转换、映射,从而更好的处理一些字段不一样的情况,用意就是一个Mapper搞定一切。

Apache BeanUtils和Apache PropertyUtils

两个工具类的使用方式差不多,都是专门用于操作Bean的工具类,目前很多流行的框架基本都离不开他。

Spring BeanUtils

BeanUtils提供对Java反射和自省API的包装。其主要目的是利用反射机制对JavaBean的属性进行处理。

Jodd BeanUtils

Jodd的BeanUtil也提供了相当方便的操作Bean的API,允许读写属性。

Cglib

cglib是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。很多熟悉的框架如spring、hibernate也使用了它。

测试比较

通过测试类来比较各个开源工具实现复制简单的javaBean的性能差异:

以下是代码的依赖包:可以到maven repository官网下载

maven依赖包

<dependency>
 <groupId>org.apache.commons</groupId>
 <artifactId>commons-lang3</artifactId>
 <version>3.3.2</version>
</dependency>

<dependency>
 <groupId>log4j</groupId>
 <artifactId>log4j</artifactId>
 <version>1.2.17</version>
</dependency>

<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-log4j12</artifactId>
 <version>1.6.1</version>
</dependency>

<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-api</artifactId>
 <version>1.7.12</version>
</dependency>

<dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>4.11</version>
 <scope>test</scope>
</dependency>

<dependency>
 <groupId>com.google.guava</groupId>
 <artifactId>guava</artifactId>
 <version>16.0.1</version>
</dependency>

<dependency>
 <groupId>org.jodd</groupId>
 <artifactId>jodd-bean</artifactId>
 <version>3.4.9</version>
 <scope>test</scope>
</dependency>

<dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib-nodep</artifactId>
 <version>3.1</version>
</dependency>

<dependency>
 <groupId>commons-beanutils</groupId>
 <artifactId>commons-beanutils</artifactId>
 <version>1.9.0</version>
 <scope>test</scope>
</dependency>

<dependency>
 <groupId>net.sf.dozer</groupId>
 <artifactId>dozer</artifactId>
 <version>5.4.0</version>
 <scope>test</scope>
</dependency>

<dependency>
 <groupId>org.assertj</groupId>
 <artifactId>assertj-core</artifactId>
 <version>1.5.0</version>
</dependency>

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

<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context-support</artifactId>
 <version>3.2.5.RELEASE</version>
</dependency>

源代码

import java.util.Date;
import jodd.bean.BeanCopy;
import net.sf.cglib.beans.BeanCopier;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.dozer.DozerBeanMapper;
import org.dozer.Mapper;
import org.junit.Test;
/**
*A、B类是普通javaBean类
**/
public class BeanCopierPerformanceTest {
    private A getA() {
        A a = new A();
        a.setF1(1);
        a.setF2(1);
        a.setF3("aaaa");
        a.setF4(new Date());
        a.setF5("bbbb");
        return a;
    }
    @Test
    public void testSet() throws Exception {
        A a = getA();
        long t1 = System.currentTimeMillis();
        for (int i=0;i<1000000;i++){
            B b = new B();
            b.setF1(a.getF1());
            b.setF2(a.getF2());
            b.setF3(a.getF3());
            b.setF4(a.getF4());
            b.setF5(a.getF5());
        }
        long t2 = System.currentTimeMillis();
        System.out.println("get/set time takes " + (t2 - t1));
    }
    @Test
    public void testJodd() throws Exception {
        A a = getA();
        long t1 = System.currentTimeMillis();
        for (int i=0;i<1000000;i++){
            B b = new B();
            BeanCopy beans = BeanCopy.beans(a, b);
            beans.copy();
        }
        long t2 = System.currentTimeMillis();
        System.out.println("Jodd time takes "+(t2-t1));
    }
    @Test
    public void testCglib() throws Exception {
        A a = getA();
        long t1 = System.currentTimeMillis();
        BeanCopier beanCopier = BeanCopier.create(A.class, B.class, false);
        for (int i=0;i<1000000;i++){
            B b = new B();
            beanCopier.copy(a,b,null);
        }
        long t2 = System.currentTimeMillis();
        System.out.println("Cglib time takes " + (t2 - t1));
    }
    @Test
    public void testBeanUtils() throws Exception {
        A a = getA();
        long t1 = System.currentTimeMillis();
        for (int i=0;i<1000000;i++){
            B b = new B();
            BeanUtils.copyProperties(a, b);
        }
        long t2 = System.currentTimeMillis();
        System.out.println("BeanUtils time takes " + (t2 - t1));
    }
    @Test
    public void testDozerMapper() throws Exception {
        A a = getA();
        Mapper mapper = new DozerBeanMapper();
        long t1 = System.currentTimeMillis();
        for (int i=0;i<1000000;i++){
            B b = mapper.map(a, B.class);
        }
        long t2 = System.currentTimeMillis();
        System.out.println("Dozer time takes " + (t2 - t1));
    }
    @Test
    public void testPropertyUtils() throws Exception{
        A a = getA();
        B b =new B();
        long t1 = System.currentTimeMillis();
        for (int i=0;i<1000000;i++){
            PropertyUtils.copyProperties(b, a);
        }
        long t2 = System.currentTimeMillis();
        System.out.println("PropertyUtils time takes " + (t2 - t1));
    }
    @Test
    public void testSpringUtils(){
        A a = getA();
        B b =new B();
        long t1 = System.currentTimeMillis();
        for (int i=0;i<1000000;i++){
         org.springframework.beans.BeanUtils.copyProperties(a,b);
        }
        long t2 = System.currentTimeMillis();
        System.out.println("spring beanutils time takes " + (t2 - t1));
    }
}

测试结果

结果是以毫秒为单位

复制次数 get/set DozerMapper Apache BeanUtils Apache PropertyUtils Spring BeanUtils Jodd BeanUtils Cglib
100次 0 125 16 15 31 110 84
1000次 0 299 47 32 84 99 79
10000次 15 936 203 110 62 156 47
1000000次 32 44842 16880 8503 3298 6320 79

总结

jdk的直接写set/get是最快的,所以在性能要求高的场景下倒是不妨自己写。另外这样写也是对重构比较友好,这是其他几个工具都做不到的。性能其次是用了字节码生成的cglib,然后将其他的库远远甩在后面,接着spring的BeanUtils,相比于其它库性能也不错。而其他的库性能相差不大,大约1000次拷贝会消耗数毫秒时间,对于性能敏感的应用,特别是一些批量请求,消耗还是比较大的。

参考资料

1.dozer-初识 http://lishaorui.iteye.com/blog/1151513

2.关于BeanCopier的一些思考 http://my.oschina.net/flashsword/blog/404288?fromerr=JmwWOwf4

jdk-timer、spring-task、quartz的比较

这篇文章将简单介绍目前常用的定时任务框架! Continue reading

小岛经济学-读书笔记

Published on May 10, 2018

java常见的http请求库

Published on May 05, 2018