国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

為什么阿里代碼規(guī)約要求避免使用 Apache BeanUtils 進(jìn)行屬性的拷貝

raledong / 888人閱讀

摘要:于是我建議這位小伙伴使用了進(jìn)行屬性拷貝,這為我們的程序挖了一個(gè)坑阿里代碼規(guī)約當(dāng)我們開(kāi)啟阿里代碼掃描插件時(shí),如果你使用了進(jìn)行屬性拷貝,它會(huì)給你一個(gè)非常嚴(yán)重的警告。大名鼎鼎的提供的包,居然會(huì)存在性能問(wèn)題,以致于阿里給出了嚴(yán)重的警告。

聲明:本文屬原創(chuàng)文章,始發(fā)于公號(hào):程序員自學(xué)之道,并同步發(fā)布于 https://blog.csdn.net/dadiyang,特此,同步發(fā)布到 sf,轉(zhuǎn)載請(qǐng)注明出處。

緣起

有一次開(kāi)發(fā)過(guò)程中,剛好看到一個(gè)小伙伴在調(diào)用 set 方法,將一個(gè)數(shù)據(jù)庫(kù)中查詢出來(lái)的 PO 對(duì)象的屬性拷貝到 Vo 對(duì)象中,類似這樣:

可以看出,Po 和 Vo 兩個(gè)類的字段絕大部分是一樣的,我們一個(gè)個(gè)地調(diào)用 set 方法只是做了一些重復(fù)的冗長(zhǎng)的操作。這種操作非常容易出錯(cuò),因?yàn)閷?duì)象的屬性太多,有可能會(huì)漏掉一兩個(gè),而且肉眼很難察覺(jué)

類似這樣的操作,我們可以很容易想到,可以通過(guò)反射來(lái)解決。其實(shí),如此普遍通用的功能,一個(gè) BeanUtils 工具類就可以搞定了。

于是我建議這位小伙伴使用了 Apache BeanUtils.copyProperties 進(jìn)行屬性拷貝,這為我們的程序挖了一個(gè)坑!

阿里代碼規(guī)約

當(dāng)我們開(kāi)啟阿里代碼掃描插件時(shí),如果你使用了 Apache BeanUtils.copyProperties 進(jìn)行屬性拷貝,它會(huì)給你一個(gè)非常嚴(yán)重的警告。因?yàn)椋?strong>Apache BeanUtils性能較差,可以使用 Spring BeanUtils 或者 Cglib BeanCopier 來(lái)代替。

看到這樣的警告,有點(diǎn)讓人有點(diǎn)不爽。大名鼎鼎的 Apache 提供的包,居然會(huì)存在性能問(wèn)題,以致于阿里給出了嚴(yán)重的警告。

那么,這個(gè)性能問(wèn)題究竟是有多嚴(yán)重呢?畢竟,在我們的應(yīng)用場(chǎng)景中,如果只是很微小的性能損耗,但是能帶來(lái)非常大的便利性,還是可以接受的。

帶著這個(gè)問(wèn)題。我們來(lái)做一個(gè)實(shí)驗(yàn),驗(yàn)證一下。

如果對(duì)具體的測(cè)試方式?jīng)]有興趣,可以跳過(guò)直接看結(jié)果哦~

測(cè)試方法接口和實(shí)現(xiàn)定義

首先,為了測(cè)試方便,讓我們來(lái)定義一個(gè)接口,并將幾種實(shí)現(xiàn)統(tǒng)一起來(lái):

public interface PropertiesCopier {
    void copyProperties(Object source, Object target) throws Exception;
}
public class CglibBeanCopierPropertiesCopier implements PropertiesCopier {
    @Override
    public void copyProperties(Object source, Object target) throws Exception {
        BeanCopier copier = BeanCopier.create(source.getClass(), target.getClass(), false);
        copier.copy(source, target, null);
    }
}
// 全局靜態(tài) BeanCopier,避免每次都生成新的對(duì)象
public class StaticCglibBeanCopierPropertiesCopier implements PropertiesCopier {
    private static BeanCopier copier = BeanCopier.create(Account.class, Account.class, false);
    @Override
    public void copyProperties(Object source, Object target) throws Exception {
        copier.copy(source, target, null);
    }
}
public class SpringBeanUtilsPropertiesCopier implements PropertiesCopier {
    @Override
    public void copyProperties(Object source, Object target) throws Exception {
        org.springframework.beans.BeanUtils.copyProperties(source, target);
    }
}
public class CommonsBeanUtilsPropertiesCopier implements PropertiesCopier {
    @Override
    public void copyProperties(Object source, Object target) throws Exception {
        org.apache.commons.beanutils.BeanUtils.copyProperties(target, source);
    }
}
public class CommonsPropertyUtilsPropertiesCopier implements PropertiesCopier {
    @Override
    public void copyProperties(Object source, Object target) throws Exception {
        org.apache.commons.beanutils.PropertyUtils.copyProperties(target, source);
    }
}
單元測(cè)試

然后寫(xiě)一個(gè)參數(shù)化的單元測(cè)試:

@RunWith(Parameterized.class)
public class PropertiesCopierTest {
    @Parameterized.Parameter(0)
    public PropertiesCopier propertiesCopier;
    // 測(cè)試次數(shù)
    private static List testTimes = Arrays.asList(100, 1000, 10_000, 100_000, 1_000_000);
    // 測(cè)試結(jié)果以 markdown 表格的形式輸出
    private static StringBuilder resultBuilder = new StringBuilder("|實(shí)現(xiàn)|100|1,000|10,000|100,000|1,000,000|
").append("|----|----|----|----|----|----|
");

    @Parameterized.Parameters
    public static Collection data() {
        Collection params = new ArrayList<>();
        params.add(new Object[]{new StaticCglibBeanCopierPropertiesCopier()});
        params.add(new Object[]{new CglibBeanCopierPropertiesCopier()});
        params.add(new Object[]{new SpringBeanUtilsPropertiesCopier()});
        params.add(new Object[]{new CommonsPropertyUtilsPropertiesCopier()});
        params.add(new Object[]{new CommonsBeanUtilsPropertiesCopier()});
        return params;
    }

    @Before
    public void setUp() throws Exception {
        String name = propertiesCopier.getClass().getSimpleName().replace("PropertiesCopier", "");
        resultBuilder.append("|").append(name).append("|");
    }

    @Test
    public void copyProperties() throws Exception {
        Account source = new Account(1, "test1", 30D);
        Account target = new Account();
        // 預(yù)熱一次
        propertiesCopier.copyProperties(source, target);
        for (Integer time : testTimes) {
            long start = System.nanoTime();
            for (int i = 0; i < time; i++) {
                propertiesCopier.copyProperties(source, target);
            }
            resultBuilder.append((System.nanoTime() - start) / 1_000_000D).append("|");
        }
        resultBuilder.append("
");
    }

    @AfterClass
    public static void tearDown() throws Exception {
        System.out.println("測(cè)試結(jié)果:");
        System.out.println(resultBuilder);
    }
}
測(cè)試結(jié)果

時(shí)間單位毫秒

實(shí)現(xiàn) 100次 1,000次 10,000次 100,000次 1,000,000次
StaticCglibBeanCopier 0.055022 0.541029 0.999478 2.754824 9.88556
CglibBeanCopier 5.320798 11.086323 61.037446 72.484607 333.384007
SpringBeanUtils 5.180483 21.328542 30.021662 103.266375 966.439272
CommonsPropertyUtils 9.729159 42.927356 74.063789 386.127787 1955.5437
CommonsBeanUtils 24.99513 170.728558 572.335327 2970.3068 27563.3459

結(jié)果表明,Cglib 的 BeanCopier 的拷貝速度是最快的,即使是百萬(wàn)次的拷貝也只需要 10 毫秒!
相比而言,最差的是 Commons 包的 BeanUtils.copyProperties 方法,100 次拷貝測(cè)試與表現(xiàn)最好的 Cglib 相差 400 倍之多。百萬(wàn)次拷貝更是出現(xiàn)了 2800 倍的性能差異!

結(jié)果真是讓人大跌眼鏡。

但是它們?yōu)槭裁磿?huì)有這么大的差異呢?

原因分析

查看源碼,我們會(huì)發(fā)現(xiàn) CommonsBeanUtils 主要有以下幾個(gè)耗時(shí)的地方:

輸出了大量的日志調(diào)試信息

重復(fù)的對(duì)象類型檢查

類型轉(zhuǎn)換

  public void copyProperties(final Object dest, final Object orig)
        throws IllegalAccessException, InvocationTargetException {
        // 類型檢查 
        if (orig instanceof DynaBean) {
            ...
        } else if (orig instanceof Map) {
           ...
        } else {
            final PropertyDescriptor[] origDescriptors = ...
            for (PropertyDescriptor origDescriptor : origDescriptors) {
                ...
                // 這里每個(gè)屬性都調(diào)一次 copyProperty
                copyProperty(dest, name, value);
            }
        }
    }

    public void copyProperty(final Object bean, String name, Object value)
        throws IllegalAccessException, InvocationTargetException {
        ...
        // 這里又進(jìn)行一次類型檢查
        if (target instanceof DynaBean) {
            ...
        }
        ...
        // 需要將屬性轉(zhuǎn)換為目標(biāo)類型
        value = convertForCopy(value, type);
        ...
    }
    // 而這個(gè) convert 方法在日志級(jí)別為 debug 的時(shí)候有很多的字符串拼接
    public  T convert(final Class type, Object value) {
        if (log().isDebugEnabled()) {
            log().debug("Converting" + (value == null ? "" : " "" + toString(sourceType) + """) + " value "" + value + "" to type "" + toString(targetType) + """);
        }
        ...
        if (targetType.equals(String.class)) {
            return targetType.cast(convertToString(value));
        } else if (targetType.equals(sourceType)) {
            if (log().isDebugEnabled()) {
                log().debug("No conversion required, value is already a " + toString(targetType));
            }
            return targetType.cast(value);
        } else {
            // 這個(gè) convertToType 方法里也需要做類型檢查
            final Object result = convertToType(targetType, value);
            if (log().isDebugEnabled()) {
                log().debug("Converted to " + toString(targetType) + " value "" + result + """);
            }
            return targetType.cast(result);
        }
    }

具體的性能和源碼分析,可以參考這幾篇文章:

幾種copyProperties工具類性能比較:https://www.jianshu.com/p/bcb...

CGLIB中BeanCopier源碼實(shí)現(xiàn):https://www.jianshu.com/p/f8b...

Java Bean Copy框架性能對(duì)比:https://yq.aliyun.com/article...

One more thing

除了性能問(wèn)題之外,在使用 CommonsBeanUtils 時(shí)還有其他的坑需要特別小心!

包裝類默認(rèn)值

在進(jìn)行屬性拷貝時(shí),雖然 CommonsBeanUtils 默認(rèn)不會(huì)給原始包裝類賦默認(rèn)值的,但是在使用低版本(1.8.0及以下)的時(shí)候,如果你的類有 Date 類型屬性,而且來(lái)源對(duì)象中該屬性值為 null 的話,就會(huì)發(fā)生異常:

org.apache.commons.beanutils.ConversionException: No value specified for "Date"

解決這個(gè)問(wèn)題的辦法是注冊(cè)一個(gè) DateConverter:

ConvertUtils.register(new DateConverter(null), java.util.Date.class);

然而這個(gè)語(yǔ)句,會(huì)導(dǎo)致包裝類型會(huì)被賦予原始類型的默認(rèn)值,如 Integer 屬性默認(rèn)賦值為 0,盡管你的來(lái)源對(duì)象該字段的值為 null。

在高版本(1.9.3)中,日期 null 值的問(wèn)題和包裝類賦默認(rèn)值的問(wèn)題都被修復(fù)了。

這個(gè)在我們的包裝類屬性為 null 值時(shí)有特殊含義的場(chǎng)景,非常容易踩坑!例如搜索條件對(duì)象,一般 null 值表示該字段不做限制,而 0 表示該字段的值必須為0。

改用其他工具時(shí)

當(dāng)我們看到阿里的提示,或者你看了這篇文章之后,知道了 CommonsBeanUtils 的性能問(wèn)題,想要改用 Spring 的 BeanUtils 時(shí),要小心:

org.apache.commons.beanutils.BeanUtils.copyProperties(Object target, Object source);
org.springframework.beans.BeanUtils.copyProperties(Object source, Object target);

從方法簽名上可以看出,這兩個(gè)工具類的名稱相同,方法名也相同,甚至連參數(shù)個(gè)數(shù)、類型、名稱都相同。但是參數(shù)的位置是相反的。因此,如果你想更改的時(shí)候,千萬(wàn)要記得,將 target 和 source 兩個(gè)參數(shù)也調(diào)換過(guò)來(lái)!

另外,可能由于種種原因,你獲取的堆棧信息不完整找不到問(wèn)題在哪,所以這里順便提醒一下:

如果你遇到 java.lang.IllegalArgumentException: Source must not be null 或者 java.lang.IllegalArgumentException: Target must not be null 這樣的異常信息卻到處找不到原因時(shí),不用找了,這是由于你在 copyProperties 的時(shí)候傳了 null 值導(dǎo)致的。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/74742.html

相關(guān)文章

  • JAVA代碼規(guī)范

    摘要:前言作為一名全干打字員,干活時(shí)經(jīng)常會(huì)被要求使用各種各樣的語(yǔ)言去實(shí)現(xiàn)各種各樣的需求,來(lái)回切換起來(lái)寫(xiě)的代碼就會(huì)或多或少有點(diǎn)不規(guī)范。今天我們以為例,講講在代碼中,我們需要注意的某些規(guī)范。 前言 作為一名全干打字員,干活時(shí)經(jīng)常會(huì)被要求使用各種各樣的語(yǔ)言去實(shí)現(xiàn)各種各樣的需求,來(lái)回切換起來(lái)寫(xiě)的代碼就會(huì)或多或少有點(diǎn)不規(guī)范。今天我們以JAVA為例,講講在代碼中,我們需要注意的某些規(guī)范。(本文標(biāo)準(zhǔn)依賴于...

    GHOST_349178 評(píng)論0 收藏0
  • BeanUtils工具使用細(xì)節(jié)

    摘要:拷貝操作又一個(gè)非常好用的工具類和中分別存在一個(gè),提供了對(duì)。除了支持基本類型以及基本類型的數(shù)組之外,還支持這些類的對(duì)象,其余一概不支持。而且,由于這些類都是采用反射機(jī)制實(shí)現(xiàn)的,對(duì)程序的效率也會(huì)有影響。因此,慎用或者使用看效果如何 java bean拷貝操作又一個(gè)非常好用的工具類 BeanUitls :spring (org.springframework.beans.BeanUtils)...

    afishhhhh 評(píng)論0 收藏0
  • 常識(shí)之外規(guī)范——阿里java開(kāi)發(fā)手冊(cè)筆記(全章節(jié))

    摘要:說(shuō)明這篇文章是我第一次認(rèn)真閱讀阿里巴巴開(kāi)發(fā)手冊(cè)終極版的筆記。說(shuō)明本手冊(cè)明確防止是調(diào)用者的責(zé)任。一年半載后,那么單元測(cè)試幾乎處于廢棄狀態(tài)。好的單元測(cè)試能夠最大限度地規(guī)避線上故障。 說(shuō)明 這篇文章是我第一次(認(rèn)真)閱讀《阿里巴巴 Java 開(kāi)發(fā)手冊(cè)(終極版)》的筆記。手冊(cè)本身對(duì)規(guī)范的講解已經(jīng)非常詳細(xì)了,如果你已經(jīng)有一定的開(kāi)發(fā)經(jīng)驗(yàn)并且有良好的編碼習(xí)慣和意識(shí),會(huì)發(fā)現(xiàn)大部分規(guī)范是符合常識(shí)的。所以...

    Martin91 評(píng)論0 收藏0
  • 新增16條設(shè)計(jì)規(guī)約!阿里巴巴Java開(kāi)發(fā)手冊(cè)(詳盡版)開(kāi)放下載!

    摘要:熟悉和遵守阿里巴巴開(kāi)發(fā)手冊(cè)的編程風(fēng)格,那只是標(biāo),而代碼可讀性的本可以追溯到軟件設(shè)計(jì)階段。何為條設(shè)計(jì)規(guī)約是根據(jù)阿里巴巴實(shí)際項(xiàng)目架構(gòu)經(jīng)驗(yàn)提煉而成,共條。本次新增的不單是條新的設(shè)計(jì)規(guī)約,還是千萬(wàn)阿里人的技術(shù)之心。 摘要:2018年6月,《阿里巴巴Java開(kāi)發(fā)手冊(cè)》再次刷新代碼規(guī)范認(rèn)知,我們新增了16條設(shè)計(jì)規(guī)約!現(xiàn)免費(fèi)開(kāi)放下載,不可錯(cuò)過(guò)!《阿里巴巴Java開(kāi)發(fā)手冊(cè)》是阿里內(nèi)部Java工程師所遵...

    _ang 評(píng)論0 收藏0
  • 排名前16Java工具類

    摘要:在中,工具類定義了一組公共方法,這篇文章將介紹中使用最頻繁及最通用的工具類。另外,工具類,根據(jù)阿里開(kāi)發(fā)手冊(cè),包名如果要使用不能帶,工具類命名為 在Java中,工具類定義了一組公共方法,這篇文章將介紹Java中使用最頻繁及最通用的Java工具類。以下工具類、方法按使用流行度排名,參考數(shù)據(jù)來(lái)源于Github上隨機(jī)選取的5萬(wàn)個(gè)開(kāi)源項(xiàng)目源碼。 一. org.apache.commons.io....

    android_c 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

raledong

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<