简介: 咱们到底应该抉择哪种属性拷贝类工具更加适合呢?为什么阿里巴巴Java开发手册中提到禁止应用Apache BeanUtils呢?

作者 | Hollis

在日常开发中,咱们常常须要给对象进行赋值,通常会调用其set/get办法,有些时候,如果咱们要转换的两个对象之间属性大致相同,会思考应用属性拷贝工具进行。

如咱们常常在代码中会对一个数据结构封装成DO、SDO、DTO、VO等,而这些Bean中的大部分属性都是一样的,所以应用属性拷贝类工具能够帮忙咱们节俭大量的set和get操作。

市面上有很多相似的工具类,比拟罕用的有

1、Spring BeanUtils 2、Cglib BeanCopier 3、Apache BeanUtils 4、Apache PropertyUtils 5、Dozer

那么,咱们到底应该抉择哪种工具类更加适合呢?为什么阿里巴巴Java开发手册中提到禁止应用Apache BeanUtils呢?

因为篇幅优先,对于这几种工具类的用法及区别,还有到底是什么是浅拷贝和深拷贝不在本文的探讨范畴内。

本文次要聚焦于比照这几个类库的性能问题。

性能比照

No Data No BB,咱们就来写代码来比照下这几种框架的性能状况。

代码示例如下:

首先定义一个PersonDO类:

public class PersonDO {    private Integer id;    private String name;    private Integer age;    private Date birthday;    //省略setter/getter}

再定义一个PersonDTO类:

public class PersonDTO {    private String name;    private Integer age;    private Date birthday;}

而后进行测试类的编写:

应用Spring BeanUtils进行属性拷贝:

private void mappingBySpringBeanUtils(PersonDO personDO, int times) {    StopWatch stopwatch = new StopWatch();    stopwatch.start();    for (int i = 0; i < times; i++) {        PersonDTO personDTO = new PersonDTO();        org.springframework.beans.BeanUtils.copyProperties(personDO, personDTO);    }    stopwatch.stop();    System.out.println("mappingBySpringBeanUtils cost :" + stopwatch.getTotalTimeMillis());}

其中的StopWatch用于记录代码执行工夫,不便进行比照。

应用Cglib BeanCopier进行属性拷贝:

private void mappingByCglibBeanCopier(PersonDO personDO, int times) {    StopWatch stopwatch = new StopWatch();    stopwatch.start();    for (int i = 0; i < times; i++) {        PersonDTO personDTO = new PersonDTO();        BeanCopier copier = BeanCopier.create(PersonDO.class, PersonDTO.class, false);        copier.copy(personDO, personDTO, null);    }    stopwatch.stop();    System.out.println("mappingByCglibBeanCopier cost :" + stopwatch.getTotalTimeMillis());}

应用Apache BeanUtils进行属性拷贝:

private void mappingByApacheBeanUtils(PersonDO personDO, int times)    throws InvocationTargetException, IllegalAccessException {    StopWatch stopwatch = new StopWatch();    stopwatch.start();    for (int i = 0; i < times; i++) {        PersonDTO personDTO = new PersonDTO();        BeanUtils.copyProperties(personDTO, personDO);    }    stopwatch.stop();    System.out.println("mappingByApacheBeanUtils cost :" + stopwatch.getTotalTimeMillis());}

应用Apache PropertyUtils进行属性拷贝:

private void mappingByApachePropertyUtils(PersonDO personDO, int times)    throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {    StopWatch stopwatch = new StopWatch();    stopwatch.start();    for (int i = 0; i < times; i++) {        PersonDTO personDTO = new PersonDTO();        PropertyUtils.copyProperties(personDTO, personDO);    }    stopwatch.stop();    System.out.println("mappingByApachePropertyUtils cost :" + stopwatch.getTotalTimeMillis());}

而后执行以下代码:

public static void main(String[] args)    throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {    PersonDO personDO = new PersonDO();    personDO.setName("Hollis");    personDO.setAge(26);    personDO.setBirthday(new Date());    personDO.setId(1);    MapperTest mapperTest = new MapperTest();    mapperTest.mappingBySpringBeanUtils(personDO, 100);    mapperTest.mappingBySpringBeanUtils(personDO, 1000);    mapperTest.mappingBySpringBeanUtils(personDO, 10000);    mapperTest.mappingBySpringBeanUtils(personDO, 100000);    mapperTest.mappingBySpringBeanUtils(personDO, 1000000);    mapperTest.mappingByCglibBeanCopier(personDO, 100);    mapperTest.mappingByCglibBeanCopier(personDO, 1000);    mapperTest.mappingByCglibBeanCopier(personDO, 10000);    mapperTest.mappingByCglibBeanCopier(personDO, 100000);    mapperTest.mappingByCglibBeanCopier(personDO, 1000000);    mapperTest.mappingByApachePropertyUtils(personDO, 100);    mapperTest.mappingByApachePropertyUtils(personDO, 1000);    mapperTest.mappingByApachePropertyUtils(personDO, 10000);    mapperTest.mappingByApachePropertyUtils(personDO, 100000);    mapperTest.mappingByApachePropertyUtils(personDO, 1000000);    mapperTest.mappingByApacheBeanUtils(personDO, 100);    mapperTest.mappingByApacheBeanUtils(personDO, 1000);    mapperTest.mappingByApacheBeanUtils(personDO, 10000);    mapperTest.mappingByApacheBeanUtils(personDO, 100000);    mapperTest.mappingByApacheBeanUtils(personDO, 1000000);}

失去后果如下:

工具类

执行1000次耗时

执行10000次耗时

执行100000次耗时

执行1000000次耗时

Spring BeanUtils

5ms

10ms

45ms

169ms

Cglib BeanCopier

4ms

18ms

45ms

91ms

Apache PropertyUtils

60ms

265ms

1444ms

11492ms

Apache BeanUtils

138ms

816ms

4154ms

36938ms

Dozer

566ms

2254ms

11136ms

102965ms

画了一张折线图更不便大家进行比照

综上,咱们根本能够得出结论,在性能方面,Spring BeanUtils和Cglib BeanCopier体现比拟不错,而Apache PropertyUtils、Apache BeanUtils以及Dozer则体现的很不好。

所以,如果思考性能状况的话,倡议大家不要抉择Apache PropertyUtils、Apache BeanUtils以及Dozer等工具类。

很多人会不了解,为什么赫赫有名的Apache开源进去的的类库性能确不高呢?这不像是Apache的格调呀,这背地导致性能低下的起因又是什么呢?

其实,是因为Apache BeanUtils力求做得完满, 在代码中减少了十分多的校验、兼容、日志打印等代码,适度的包装导致性能降落重大。

总结

本文通过比照几种常见的属性拷贝的类库,剖析得出了这些工具类的性能状况,最终也验证了《阿里巴巴Java开发手册》中提到的”Apache BeanUtils 效率低”的事实。

然而本文只是站在性能这一繁多角度进行了比照,咱们在抉择一个工具类的时候还会有其余方面的思考,比方应用老本、了解难度、兼容性、可扩展性等,对于这种拷贝类工具类,咱们还会思考其性能是否欠缺等。

就像尽管Dozer性能比拟差,然而他能够很好的和Spring联合,能够通过配置文件等进行属性之间的映射等,也受到了很多开发者的青睐。

本文用到的第三方类库的maven依赖如下:

<!--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.1.1.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>