Java Bean Copy元件的效能比較

hryou0922發表於2019-04-28

公司系統中之前一直有使用元件進行Bean copy的操作,只是知道此操作對效能有影響,但是到底有多少影響心裡一直沒有數。現在對Bean copy進行測試獲取量化的結果

目前Bean Copy的主流元件:

  • Apache BeanUtils

  • Spring BeanUtils

  • Cglib BeanCopier

    眾所周知Apache BeanUtils效能太差,一般不推薦使用。這裡不對其進行測試,只測試後面兩個元件

測試環境:

  • JAVA8
  • Spring Boot 2.1.4.RELEASE
  • 本地普通桌上型電腦

測試程式碼:

測試功能:迴圈N次,將TelAppModel 物件中的屬性複製到TelAppDto中,統計每種元件花費的時間,花費時間越少的效能越強。

測試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方法 > Sprign BeanUtils > Cglib BeanCopier
  • ○ 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
複製程式碼

相關文章