Java Bean Copy組件的性能比較

公司系統中以前一直有使用組件進行Bean copy的操做,只是知道此操做對性能有影響,可是到底有多少影響內心一直沒有數。如今對Bean copy進行測試獲取量化的結果java

目前Bean Copy的主流組件:spring

  • Apache BeanUtils緩存

  • Spring BeanUtilsbash

  • Cglib BeanCopier多線程

    衆所周知Apache BeanUtils性能太差,通常不推薦使用。這裏不對其進行測試,只測試後面兩個組件app

測試環境:

  • JAVA8
  • Spring Boot 2.1.4.RELEASE
  • 本地普通臺式機

測試代碼:

測試功能:循環N次,將TelAppModel 對象中的屬性複製到TelAppDto中,統計每種組件花費的時間,花費時間越少的性能越強。oop

測試POJO類 源類 TelAppModel.java 和目標類 TelAppDto.java,兩個類都是簡單的pojo類且成員變量相同。性能

普通的Java set/get方法實現Bean Copy, 代碼以下:測試

public static void copySetGet(TelAppModel source, TelAppDto target){
       target.setId(source.getId());
       target.setTelPowerSavingMode((byte)0);
       target.setTelSecret(source.getTelSecret());
       target.setTelAppId(source.getTelAppId());
       target.setTelName(source.getTelName());
       target.setDesc(source.getDesc());
   }
複製代碼

Spring BeanUtils實現Bean Copy代碼以下:優化

public static void copyPropertiesSpring(Object source, Object target){
    BeanUtils.copyProperties(source, target);
}
複製代碼

Cglib BeanCopier實現Bean Copy代碼以下:

public static void copyPropertiesCglib(Object source, Object target){
    BeanCopier beanCopier = BeanCopier.create(source.getClass(), target.getClass(), false);
    beanCopier.copy(source, target, null);
}
複製代碼

測試代碼: longCount:定義複製執行的次數

// 在執行Bean copy前 先初始化 longCount 個TelAppModel,作爲測試素材

for(int i = 0; i < longCount; i++){
      appModelSourceList.add(createModel());
}
複製代碼

3種Bean copy方法依次調用此test()方法:

// set/get方法
test((a,b) -> copySetGet(a, b));

// spring BeanUtils
test((a,b) -> copyPropertiesSpring(a, b));

// spring BeanCopier
test((a,b) -> copyPropertiesCglib(a, b));
複製代碼

// 此方法依次將列表中TelAppModel對象複製到TelAppDto對象中,並打印執行longCount次花費的時間,爲了保證結果準確,以上操做執行3次,即3*longCount次

private void test(BiConsumer<TelAppModel, TelAppDto> biConsumer){
    int runNum = 3;
    for(int k = 0; k < runNum; k++) {
        long loopCount = longCount;
        long begin = System.currentTimeMillis();
        for (int i = 0; i < loopCount; i++) {
            TelAppModel telAppModel1 = appModelSourceList.get(i);
            TelAppDto telAppDto = new TelAppDto();
            biConsumer.accept(telAppModel1, telAppDto);
        }
        System.out.println((System.currentTimeMillis() - begin));
    }
}
複製代碼

測試報告:

分別執行1000、10000、100000、1000000次耗時數(毫秒): 詳細時間以下:

在這裏插入圖片描述
數據分析

  • ○ set/get方法理論上應該是最快的
  • ○ 性能以下: set/get方法 > Cglib BeanCopier > Sprign BeanUtils
  • ○ Spring BeanUtils每次第一次循環花費時間特別多,和後面二次不在一個數據量,數據有異常

在執行命令時,經過jvisualvm查看CPU的耗時時間,詳細以下:

在這裏插入圖片描述
圖表分析:

  • Spring BeanUtils第一次執行CachedIntrospectionResults.forClass()方法時,會將class信息從磁盤加載到內存緩存,而後後續的操做會從緩存中獲取class信息。這也解釋了爲何Spring BeanUtils爲何會比較慢的緣由。另外此操做是同步的,若是多線程同時加載相同的class,會出現阻塞的狀況。
  • Spring BeanUtils的copyProperties佔用的CPU也比較多,說明真正執行copy Bean操做依然會花費較多的時間
  • 觀察上圖,發現BeanCopier主要花費的時間是調用create()方法,真正執行copy Bean花費的時間較少,如今咱們根據此進行優化,執行Bean Copy時,不是每次執行建立新BeanCopier,而是使用將BeanCopier.create()建立的BeanCopier進行緩存,修改後代碼以下:
static BeanCopier beanCopierStatic = BeanCopier.create(TelAppModel.class, TelAppDto.class, false);
 
 public static void copyPropertiesCglibStatic(Object source, Object target){
     beanCopierStatic.copy(source, target, null);
 }
複製代碼

再分別執行1000、10000、100000、1000000次耗時數(毫秒): 詳細時間以下:

在這裏插入圖片描述

分析: Cglib BeanCopier優化,性能大大提升。cglib在性能和set/get方法相差不大

結論:

spring beanUtils 和 cglib 性能都還能夠接受,若是對性能沒有很是苛刻的要求,使用cglib或spring bean utils 問題都問題不大,推薦優先使用cglib
複製代碼
相關文章
相關標籤/搜索