性能優化 ---- 避免使用 BeanUtils copy

一、背景

在開發過程中,我們經常會遇到對象的轉換,比如外部的 DTO 對象轉換爲內部的 DO 對象,這裏面很多字段名都是相等的,要是一個一個去 get/set 很多人會覺得很煩,於是爲了方便和代碼的簡介大家不約而同地找到了 UtilsBean.copy 相關的對象屬性copy工具包。

當我們系統還只是 QPS 幾十上百時,可能我們對系統的性能優化還不用到這麼細緻,當我們的系統 QPS/TPS 達到幾十萬上百萬的時候,UtilsBean.copy 的開銷問題就會凸顯。

 

二、Apache、Spring、CGLib、Getter/Setter 屬性copy 性能對比

package test.bean.copy;



import net.sf.cglib.beans.BeanCopier;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class BeanCopyTest {


    public static void main(String[] args) throws Exception{
        TargetDTO targetDTO = new TargetDTO();
        SourceDTO sourceDTO = new SourceDTO();
        sourceDTO.setAge(18);
        sourceDTO.setName("wenniuwuren");
        int totalTimes = 10000;


        /**
         * Apache BeanUtils
         */
        long startTime1 = System.currentTimeMillis();
        for (int i = 0; i < totalTimes; i++) {
            org.apache.commons.beanutils.BeanUtils.copyProperties(targetDTO, sourceDTO);
        }
        System.out.println("Apache BeanUtils single thread cost:" + (System.currentTimeMillis() - startTime1) + targetDTO.getName() + targetDTO.getAge());

        ExecutorService executorService4 = Executors.newFixedThreadPool(3);
        long startTime4 = System.currentTimeMillis();
        for (int i = 0; i < totalTimes; i++) {
            executorService4.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        org.apache.commons.beanutils.BeanUtils.copyProperties(new TargetDTO(), sourceDTO);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        executorService4.shutdown();
        while (true) {
            if (executorService4.isTerminated()) {
                System.out.println("Apache BeanUtils muti thread cost:" + (System.currentTimeMillis() - startTime4));
                break;
            }
        }




        /**
         * Spring BeanUtils
         */
        targetDTO = new TargetDTO();
        long startTime2 = System.currentTimeMillis();
        for (int i = 0; i < totalTimes; i++) {
            org.springframework.beans.BeanUtils.copyProperties(sourceDTO, targetDTO);
        }
        System.out.println("Spring BeanUtils single thread cost:" + (System.currentTimeMillis() - startTime2) + targetDTO.getName() + targetDTO.getAge());

        ExecutorService executorService5 = Executors.newFixedThreadPool(3);
        long startTime5 = System.currentTimeMillis();
        for (int i = 0; i < totalTimes; i++) {
            executorService5.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        org.springframework.beans.BeanUtils.copyProperties(sourceDTO, new TargetDTO());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        executorService5.shutdown();
        while (true) {
            if (executorService5.isTerminated()) {
                System.out.println("Spring BeanUtils muti thread cost:" + (System.currentTimeMillis() - startTime5));
                break;
            }
        }


        /**
         * CGLib BeanUtils
         */
        targetDTO = new TargetDTO();
        long startTime3 = System.currentTimeMillis();
        BeanCopier beanCopier = BeanCopier.create(SourceDTO.class, TargetDTO.class, false);
        for (int i = 0; i < totalTimes; i++) {
            beanCopier.copy(sourceDTO, targetDTO, null);
        }
        System.out.println("CGLib BeanUtils cost:" + (System.currentTimeMillis() - startTime3) + targetDTO.getName() + targetDTO.getAge());

        ExecutorService executorService6 = Executors.newFixedThreadPool(3);
        long startTime6 = System.currentTimeMillis();
        for (int i = 0; i < totalTimes; i++) {
            executorService6.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        beanCopier.copy(sourceDTO, new TargetDTO(), null);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        executorService6.shutdown();
        while (true) {
            if (executorService6.isTerminated()) {
                System.out.println("CGLib BeanUtils muti thread cost:" + (System.currentTimeMillis() - startTime6));
                break;
            }
        }


        /**
         * Getter Setter
         */
        long startTime7 = System.currentTimeMillis();
        for (int i = 0; i < totalTimes; i++) {
            targetDTO.setAge(sourceDTO.getAge());
            targetDTO.setName(sourceDTO.getName());
        }
        System.out.println("GetterSetter single thread cost:" + (System.currentTimeMillis() - startTime7) + targetDTO.getName() + targetDTO.getAge());

        ExecutorService executorService8 = Executors.newFixedThreadPool(3);
        long startTime8 = System.currentTimeMillis();
        for (int i = 0; i < totalTimes; i++) {
            executorService8.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        final TargetDTO targetDTO = new TargetDTO();
                        targetDTO.setAge(sourceDTO.getAge());
                        targetDTO.setName(sourceDTO.getName());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        executorService8.shutdown();
        while (true) {
            if (executorService8.isTerminated()) {
                System.out.println("GetterSetter muti thread cost:" + (System.currentTimeMillis() - startTime8));
                break;
            }
        }

    }




}

 

運行結果:

Apache BeanUtils single thread cost:216nullnull
Apache BeanUtils muti thread cost:62
Spring BeanUtils single thread cost:168wenniuwuren18
Spring BeanUtils muti thread cost:18
CGLib BeanUtils cost:72wenniuwuren18
CGLib BeanUtils muti thread cost:8
GetterSetter single thread cost:0wenniuwuren18
GetterSetter muti thread cost:12

 

 

三、結論

從上述結果可以看出來,性能上 Getter/Setter > CGLib > Spring > Apache


優點
1、簡潔方便,耗時短

缺點
1、性能較差,因爲 BeanUtils.copy 實現是通過java反射機制
2、引用查找難,BeanUtils.copy的實現會隱藏對象屬性的設置的調用,想查看target屬性A有哪些地方被設值了,那麼通過IDE查找引用會漏掉BeanUtils.copy的設置

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章