爲何阿里巴巴禁止使用Apache Beanutils進行屬性的copy?

在平常開發中,咱們常常須要給對象進行賦值,一般會調用其set/get方法,有些時候,若是咱們要轉換的兩個對象之間屬性大體相同,會考慮使用屬性拷貝工具進行。node

如咱們常常在代碼中會對一個數據結構封裝成DO、SDO、DTO、VO等,而這些Bean中的大部分屬性都是同樣的,因此使用屬性拷貝類工具能夠幫助咱們節省大量的set和get操做。spring

市面上有不少相似的工具類,比較經常使用的有api

一、Spring BeanUtils數據結構

二、Cglib BeanCopierapp

三、Apache BeanUtils框架

四、Apache PropertyUtilsmaven

五、Dozeride

那麼,咱們到底應該選擇哪一種工具類更加合適呢?爲何阿里巴巴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>

【責任編輯:龐桂玉 TEL:(010)68476606】

相關文章
相關標籤/搜索