前言
在日常开发中,不可避免的遇到对象属性相同的情况,为了方便,不通过setter,进行一个个的字段赋值,而是通过BeanUtils进行copy。快速的实现了,对象属性值得copy、赋值。
因此,各种技术框架都实现了自己的BeanUtils。比较知名的,包括:
1、Spring BeanUtils 2、Cglib BeanCopier 3、Apache BeanUtils 4、Apache PropertyUtils 5、Dozer
那么,到底哪个更优秀,没有bug,值得我们推广使用呢?先放着这个问题在心里,众所周知,阿里的开发者手册,愈来愈成为一种开发行为规范,被业内广泛认可。目前,已经是经过了好几个版本的更新。目前是嵩山版,有需要的,可以到阿里云开发者社区,下载。官网链接: https://developer.aliyun.com
在开发者手册中,提到了以下内容
那么,到底是为什么把Apache Beanutils给pass了呢?带着上述两个问题,开启我们今天学习的征程。
性能对比
我们将对上述几种属性copy方式,分别测试,统计耗时,对比其各中性能。
测试对象创建。创建属性相同的两个实体类。
public class PersonDto{ private Integer id; private String name; private Integer age; private Date birthday; //省略setter/getter }
public class PersonEntity { private String name; private Integer age; private Date birthday; //省略setter/getter }
往往存在,接口传输对象与数据库存储实体对象之间,有上述类似关系。
开始测试,编写测试方法类
public class CopyTest { // 使用Spring BeanUtils进行属性拷贝 public static void copyBySpringBeanUtils(PersonDto personDto, int times) { long start = System.currentTimeMillis(); for (int i = 0; i < times; i++) { PersonEntity personEntity = new PersonEntity(); org.springframework.beans.BeanUtils.copyProperties(personDto, personEntity); } long end = System.currentTimeMillis(); System.out.println("SpringBeanUtils复制次数:" + times + " ,耗时:" + (end - start) + "ms"); } // 使用CglibBeanCopier进行属性拷贝: public static void copyByCglibBeanCopier(PersonDto personDto, int times) { long start = System.currentTimeMillis(); for (int i = 0; i < times; i++) { PersonEntity personEntity = new PersonEntity(); BeanCopier copier = BeanCopier.create(PersonDto.class, PersonEntity.class, false); copier.copy(personDto, personEntity, null); } long end = System.currentTimeMillis(); System.out.println("CglibBeanCopier复制次数:" + times + " ,耗时:" + (end - start) + "ms"); } // 使用ApacheBeanUtils进行属性拷贝: public static void copyByApacheBeanUtils(PersonDto personDto, int times) throws InvocationTargetException, IllegalAccessException { long start = System.currentTimeMillis(); for (int i = 0; i < times; i++) { PersonEntity personEntity = new PersonEntity(); BeanUtils.copyProperties(personDto, personEntity); } long end = System.currentTimeMillis(); System.out.println("ApacheBeanUtils复制次数:" + times + " ,耗时:" + (end - start) + "ms"); } // 使用ApachePropertyUtils进行属性拷贝: public static void copyByApachePropertyUtils(PersonDto personDto, int times) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { long start = System.currentTimeMillis(); for (int i = 0; i < times; i++) { PersonEntity personEntity = new PersonEntity(); PropertyUtils.copyProperties(personDto, personEntity); } long end = System.currentTimeMillis(); System.out.println("ApachePropertyUtils复制次数:" + times + " ,耗时:" + (end - start) + "ms"); } }
编写执行类
public class CopyTestExec { public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { PersonDto personDto = new PersonDto(); personDto.setId(1); personDto.setName("小隐乐乐"); personDto.setAge(18); personDto.setBirthday(new Date()); CopyTest.copyBySpringBeanUtils(personDto, 100); CopyTest.copyBySpringBeanUtils(personDto, 1000); CopyTest.copyBySpringBeanUtils(personDto, 10000); CopyTest.copyBySpringBeanUtils(personDto, 100000); CopyTest.copyBySpringBeanUtils(personDto, 1000000); CopyTest.copyByCglibBeanCopier(personDto, 100); CopyTest.copyByCglibBeanCopier(personDto, 1000); CopyTest.copyByCglibBeanCopier(personDto, 10000); CopyTest.copyByCglibBeanCopier(personDto, 100000); CopyTest.copyByCglibBeanCopier(personDto, 1000000); CopyTest.copyByApacheBeanUtils(personDto, 100); CopyTest.copyByApacheBeanUtils(personDto, 1000); CopyTest.copyByApacheBeanUtils(personDto, 10000); CopyTest.copyByApacheBeanUtils(personDto, 100000); CopyTest.copyByApacheBeanUtils(personDto, 1000000); CopyTest.copyByApachePropertyUtils(personDto, 100); CopyTest.copyByApachePropertyUtils(personDto, 1000); CopyTest.copyByApachePropertyUtils(personDto, 10000); CopyTest.copyByApachePropertyUtils(personDto, 100000); CopyTest.copyByApachePropertyUtils(personDto, 1000000); } }
执行结果如下:
SpringBeanUtils 复制次数:100 ,耗时:120ms SpringBeanUtils 复制次数:1000 ,耗时:8ms SpringBeanUtils 复制次数:10000 ,耗时:20ms SpringBeanUtils 复制次数:100000 ,耗时:57ms SpringBeanUtils 复制次数:1000000 ,耗时:242ms CglibBeanCopier 复制次数:100 ,耗时:82ms CglibBeanCopier 复制次数:1000 ,耗时:9ms CglibBeanCopier 复制次数:10000 ,耗时:20ms CglibBeanCopier 复制次数:100000 ,耗时:64ms CglibBeanCopier 复制次数:1000000 ,耗时:102ms ApacheBeanUtils 复制次数:100 ,耗时:60ms ApacheBeanUtils 复制次数:1000 ,耗时:29ms ApacheBeanUtils 复制次数:10000 ,耗时:62ms ApacheBeanUtils 复制次数:100000 ,耗时:243ms ApacheBeanUtils 复制次数:1000000 ,耗时:2282ms ApachePropertyUtils复制次数:100 ,耗时:0ms ApachePropertyUtils复制次数:1000 ,耗时:2ms ApachePropertyUtils复制次数:10000 ,耗时:21ms ApachePropertyUtils复制次数:100000 ,耗时:190ms ApachePropertyUtils复制次数:1000000 ,耗时:1941ms
根据结果,我们基本可以得出结论,在性能方面,Spring BeanUtils和Cglib BeanCopier表现比较不错,而Apache PropertyUtils、Apache BeanUtils则表现的很不好。
所以,如果考虑性能情况的话,建议大家不要选择Apache PropertyUtils、Apache BeanUtils以及Dozer等工具类。
很多人会不理解,为什么大名鼎鼎的Apache开源出来的的类库性能确不高呢?这不像是Apache的风格呀,这背后导致性能低下的原因又是什么呢?
其实,是因为Apache BeanUtils力求做得完美, 在代码中增加了非常多的校验、兼容、日志打印等代码,过度的包装导致性能下降严重。
依赖
<dependencies> <!--Apache PropertyUtils、Apache BeanUtils--> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.9.4</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.2</version> </dependency> <!--Spring PropertyUtils--> <dependency> <groupId>org.springframework</groupId> <artifactId>org.springframework.beans</artifactId> <version>3.2.2.RELEASE</version> </dependency> <!--cglib--> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>2.2.2</version> </dependency> <!--dozer--> <dependency> <groupId>net.sf.dozer</groupId> <artifactId>dozer</artifactId> <version>5.5.1</version> </dependency> <!--日志相关--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jul-to-slf4j</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> <version>1.7.7</version> </dependency> </dependencies>
总结
通过本文测试,回答了前言中两个问题,明确了为何开发者手册中的强调,不使用Apache BeanUtils