概述
两个相似的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