在平常開發中,咱們常常須要給對象進行賦值,一般會調用其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】