乐趣区

关于java:为什么阿里巴巴禁止使用Apache-Beanutils进行属性的copy

简介: 咱们到底应该抉择哪种属性拷贝类工具更加适合呢?为什么阿里巴巴 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>
退出移动版