自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

對(duì)象屬性拷貝,到底誰更強(qiáng)?

開發(fā) 前端
MapStruct 也是一款對(duì)象屬性復(fù)制的工具,但是它跟我們上面介紹的幾款工具技術(shù)實(shí)現(xiàn)思路都不一樣,主要區(qū)別在于:無論是Beanutils?還是BeanCopier?,都是程序運(yùn)行期間去執(zhí)行對(duì)象屬性復(fù)制操作;而MapStruct是在程序編譯期間,就已經(jīng)生成好了對(duì)象屬性復(fù)制相關(guān)的邏輯。

一、摘要

日常編程中,我們經(jīng)常會(huì)碰到對(duì)象屬性復(fù)制的場(chǎng)景,當(dāng)類的屬性數(shù)量只有簡(jiǎn)單的幾個(gè)時(shí),我們通過手寫set/get即可完成,但是屬性有十幾個(gè),甚至幾十個(gè)的時(shí)候,通過set/get的方式,可能會(huì)占用大量的編程時(shí)間,關(guān)鍵是像這樣的代碼,基本上是機(jī)械式的操作。

面對(duì)這種重復(fù)又枯燥的編程工作,很多的行業(yè)大佬,開發(fā)出了通用的對(duì)象屬性復(fù)制工具,以免去機(jī)械式的編程。

小編經(jīng)過實(shí)際的調(diào)研,發(fā)現(xiàn)目前開源市場(chǎng)上,用得比較多的對(duì)象屬性復(fù)制工具有以下幾個(gè):

  • Apache BeanUtils
  • Spring BeanUtils
  • Cglib BeanCopier
  • MapStruct

下面我們一起來看看,他們的使用方式以及性能對(duì)比,最后根據(jù)不同的使用需求,總結(jié)出如何選擇。

二、工具實(shí)踐

首先我們定義一個(gè)UserInfo類,下面我們會(huì)以此類作為對(duì)象屬性復(fù)制的案例,內(nèi)容如下:

public class UserInfo {

    /**
     * 用戶ID
     */
    private Long userId;

    /**
     * 用戶名
     */
    private String userName;

    /**
     * 密碼
     */
    private String userPwd;

    /**
     * 年齡
     */
    private Integer age;

    /**
     * 性別
     */
    private String gender;

    /**
     * 生日
     */
    private Date birthday;


    // ...set、get

    @Override
    public String toString() {
        return "UserInfo{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", userPwd='" + userPwd + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}

2.1、Apache BeanUtils

Apache BeanUtils 這個(gè)工具,相信很多人都接觸過,因?yàn)樗?Java 領(lǐng)域?qū)傩詮?fù)制這塊非常有名,早期使用的非常廣泛!

使用方式上也非常的簡(jiǎn)單,首先在項(xiàng)目中導(dǎo)入commons-beanutils包。

<!--Apache BeanUtils-->
<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
</dependency>

然后在代碼中直接導(dǎo)入org.apache.commons.beanutils.BeanUtils工具進(jìn)行對(duì)象屬性復(fù)制,樣例代碼如下:

// 原始對(duì)象
UserInfo source = new UserInfo();
// set...

// 目標(biāo)對(duì)象
UserInfo target = new UserInfo();
BeanUtils.copyProperties(target, source);
System.out.println(target.toString());

Apache BeanUtils 工具從操作使用上還是非常方便的,不過其底層源碼為了追求完美,加了過多的包裝,使用了很多反射,做了很多校驗(yàn),導(dǎo)致屬性復(fù)制時(shí)性能較差,因此阿里巴巴開發(fā)手冊(cè)上強(qiáng)制規(guī)定避免使用 Apache BeanUtils。

圖片

關(guān)于對(duì)象屬性復(fù)制性能,我們會(huì)文末向大家介紹!

2.2、Spring BeanUtils

Spring 對(duì)象屬性復(fù)制工具類,類名與 Apache 一樣,基本用法也差不多。

首先在項(xiàng)目中導(dǎo)入spring-beans包。

<!--spring BeanUtils-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.3.30.RELEASE</version>
</dependency>

同樣的,在代碼中直接導(dǎo)入org.springframework.beans.BeanUtils工具進(jìn)行對(duì)象屬性復(fù)制,樣例代碼如下:

// 原始對(duì)象
UserInfo source = new UserInfo();
// set...

// 目標(biāo)對(duì)象
UserInfo target = new UserInfo();
BeanUtils.copyProperties(source, target);
System.out.println(target.toString());

除此之外,Spring BeanUtils 還提供了重載方法。

public static void copyProperties(Object source, Object target, String... ignoreProperties);

如果我們不想對(duì)象中的某些屬性被復(fù)制過去,可以通過如下方式實(shí)現(xiàn)。

BeanUtils.copyProperties(source, target, "userPwd");

雖然Apache BeanUtils和Spring BeanUtils使用起來都很方便,但是兩者性能差異非常大,Spring BeanUtils的對(duì)象屬性復(fù)制速度比Apache BeanUtils要快很多,主要原因在于 Spring 并沒有像 Apache 一樣使用反射做過多的參數(shù)校驗(yàn),另外Spring BeanUtils內(nèi)部使用了緩存,加快了轉(zhuǎn)換的速度。

還有一個(gè)需要注意的地方是,Apache BeanUtils和Spring BeanUtils的類名和方法基本上相同,但是它們的原始對(duì)象和目標(biāo)對(duì)象的參數(shù)位置是相反的,如果直接從Apache BeanUtils切換到Spring BeanUtils有巨大的風(fēng)險(xiǎn),如果有這個(gè)方面的需要,請(qǐng)一個(gè)一個(gè)的替換!

2.3、Cglib BeanCopier

Cglib BeanCopier 對(duì)象屬性復(fù)制工具類,相比Spring BeanUtils和Apache BeanUtils,用法稍微要多一步代碼。

首先在項(xiàng)目中導(dǎo)入cglib包。

<!--cglib-->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

然后在代碼中直接導(dǎo)入net.sf.cglib.beans.BeanCopier工具進(jìn)行對(duì)象屬性復(fù)制,樣例代碼如下:

// 原始對(duì)象
UserInfo source = new UserInfo();
// set...

// 獲取一個(gè)復(fù)制工具
BeanCopier beanCopier = BeanCopier.create(UserInfo.class, UserInfo.class, false);

// 對(duì)象屬性值復(fù)制
UserInfo target = new UserInfo();
beanCopier.copy(source, target, null);
System.out.println(target.toString());

如果遇到字段名相同,但是類型不一致的對(duì)象復(fù)制,可以引入轉(zhuǎn)換器,進(jìn)行類型轉(zhuǎn)換,比如這樣:

UserInfo source = new UserInfo();
// set...

// 創(chuàng)建一個(gè)復(fù)制工具
BeanCopier beanCopier = BeanCopier.create(UserInfo.class, UserInfo.class, true);

// 自定義對(duì)象屬性值復(fù)制
UserInfo target = new UserInfo();
beanCopier.copy(source, target, new Converter() {
    @Override
    public Object convert(Object source, Class target, Object context) {
        if(source instanceof Integer){
            return String.valueOf(source);
        }
        return source;
    }
});
System.out.println(target.toString());

Cglib BeanCopier 的工作原理與上面兩個(gè)Beanutils原理不太一樣,其主要使用字節(jié)碼技術(shù)動(dòng)態(tài)生成一個(gè)代理類,通過代理類來實(shí)現(xiàn)get/set方法。

雖然生成代理類過程存在一定開銷,但是一旦生成可以重復(fù)使用,因此 Cglib  性能相比以上兩種 Beanutils 性能都要好。

另外就是,如果你的工程是基于 Spring 框架開發(fā)的,查找 BeanCopier 這個(gè)類的時(shí)候,可以發(fā)現(xiàn)兩個(gè)不同的包,一個(gè)屬于Cglib,另一個(gè)屬于Spring-Core。

其實(shí)Spring-Core內(nèi)置的BeanCopier引入了 Cglib 中的類,這么做的目的是為保證 Spring 中使用 Cglib 相關(guān)類的穩(wěn)定性,防止外部 Cglib 依賴不一致,導(dǎo)致 Spring 運(yùn)行異常,因此無論你引用那個(gè)包,本質(zhì)都是使用 Cglib。

2.4、MapStruct

MapStruct 也是一款對(duì)象屬性復(fù)制的工具,但是它跟我們上面介紹的幾款工具技術(shù)實(shí)現(xiàn)思路都不一樣,主要區(qū)別在于:無論是Beanutils還是BeanCopier,都是程序運(yùn)行期間去執(zhí)行對(duì)象屬性復(fù)制操作;而MapStruct是在程序編譯期間,就已經(jīng)生成好了對(duì)象屬性復(fù)制相關(guān)的邏輯。

因此可以想象的到,MapStruct的復(fù)制性能要快很多!

MapStruct工具的使用也很簡(jiǎn)單,首先導(dǎo)入相關(guān)包。

<!--mapstruct 核心庫-->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.5.0.Final</version>
</dependency>

<!--mapstruct 編譯器插件-->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.5.0.Final</version>
    <scope>provided</scope>
</dependency>

然后定義一個(gè)對(duì)象屬性拷貝接口。

@Mapper
public interface UserInfoMapper {

    UserInfoMapper INSTANCE = Mappers.getMapper(UserInfoMapper.class);
    
    UserInfo copy(UserInfo source);
}

最后,在代碼中調(diào)用即可。

// 原始對(duì)象
UserInfo source = new UserInfo();
// set...

// 對(duì)象屬性復(fù)制
UserInfo target = UserInfoMapper.INSTANCE.copy(source);
System.out.println(target.toString());

MapStruct實(shí)現(xiàn)原理,剛剛也介紹了,是在編譯期間生成對(duì)象屬性復(fù)制相關(guān)的代碼邏輯,以上面的操作為例,打開class目錄文件,你會(huì)看到一個(gè)UserInfoMapperImpl實(shí)現(xiàn)類,已經(jīng)幫我們做好了set/get操作,源碼內(nèi)容如下:

public class UserInfoMapperImpl implements UserInfoMapper {
    public UserInfoMapperImpl() {
    }

    public UserInfo copy(UserInfo source) {
        if (source == null) {
            return null;
        } else {
            UserInfo userInfo = new UserInfo();
            userInfo.setUserId(source.getUserId());
            userInfo.setUserName(source.getUserName());
            userInfo.setUserPwd(source.getUserPwd());
            userInfo.setAge(source.getAge());
            userInfo.setGender(source.getGender());
            userInfo.setBirthday(source.getBirthday());
            return userInfo;
        }
    }
}

三、性能對(duì)比

了解完以上的對(duì)象屬性復(fù)制工具之后,回到我們最初發(fā)出的疑問,到底誰最強(qiáng)呢?

下面我們通過循環(huán)執(zhí)行對(duì)象屬性復(fù)制次數(shù),分別測(cè)試set/get、Apache BeanUtils、Spring BeanUtils、Cglib BeanCopier、MapStruct等執(zhí)行方法,看看它們所消耗的時(shí)間如何,統(tǒng)計(jì)報(bào)表明細(xì)如下!

圖片

從圖中我們可以得出如下結(jié)論!

  • 1.set/get方式操作最簡(jiǎn)單,性能最強(qiáng),當(dāng)之無愧 number one!但是機(jī)械式編程工作比較多。
  • 2.MapStruct方式性能其次,主要的優(yōu)勢(shì)在于編譯期間生成set/get代碼,但是操作不夠靈活,如果需要復(fù)制的目標(biāo)對(duì)象很多,需要定義多個(gè)接口或者方法。
  • 3.Cglib BeanCopier和Spring BeanUtils,雖然兩者使用的技術(shù)方案各有不同,在實(shí)際的測(cè)試過程中,100 萬數(shù)據(jù)量以下的循環(huán)復(fù)制操作差異并不明顯,編程方面比較明顯的區(qū)別是BeanCopier比BeanUtils稍微多一行代碼,日常使用中,兩者都可以,根據(jù)自己的喜好選擇即可。
  • 4.Apache BeanUtils和Spring BeanUtils,兩者的底層都是基于類反射進(jìn)行屬性復(fù)制,不同的地方在于Apache BeanUtils加了過多的包裝,使用了很多反射,做了很多校驗(yàn),導(dǎo)致性能大大削弱了,同時(shí)Spring BeanUtils內(nèi)部使用了緩存,加快了轉(zhuǎn)換的速度,因此如果要二選一,推薦采用Spring BeanUtils工具。

四、小結(jié)

本文主要圍繞對(duì)象屬性復(fù)制,從使用方面做了一次簡(jiǎn)單的內(nèi)容總結(jié)。

通過以上 5 種方式的對(duì)象屬性復(fù)制操作,給出的建議如下:

  • 1.如果當(dāng)前類只有簡(jiǎn)單的幾個(gè)屬性,建議直接使用set/get,原生編程性能最好
  • 2.如果類的屬性很多,可以使用Spring BeanUtils或者Cglib BeanCopier工具,可以省下很多的機(jī)械式編程工作
  • 3.如果當(dāng)前類屬性很多,同時(shí)對(duì)復(fù)制性能有要求,推薦使用MapStruct

最后,以上的對(duì)象屬性復(fù)制工具都是淺拷貝的實(shí)現(xiàn)方式,如果要深拷貝,可以使用對(duì)象序列戶和反序列化技術(shù)實(shí)現(xiàn)!

責(zé)任編輯:武曉燕 來源: Java極客技術(shù)
相關(guān)推薦

2018-05-22 12:08:25

華為MateBook Pr蘋果

2010-01-22 11:23:06

C++程序

2022-10-11 10:18:12

數(shù)據(jù)硬盤開機(jī)

2011-12-15 10:11:38

云安全云計(jì)算

2021-11-08 09:11:17

云計(jì)算Service Mes云應(yīng)用

2017-08-29 09:10:07

大數(shù)據(jù)第三方支付支付寶和微信

2016-11-02 09:20:01

SparkHadoop MapR大數(shù)據(jù)

2021-01-14 09:55:21

Java微服務(wù)Go

2012-02-21 22:27:56

AndroidQtQt Mobility

2011-08-29 09:45:09

2022-05-23 09:11:18

AngularBlazor前端

2013-10-31 11:28:53

瀏覽器

2017-12-14 11:19:40

IntelAMDCPU

2021-04-07 10:12:05

Javascript對(duì)象拷貝開發(fā)

2019-03-01 09:36:25

ReactAngular開發(fā)

2021-01-12 09:22:32

攜號(hào)轉(zhuǎn)網(wǎng)工信部運(yùn)營商

2014-07-16 09:15:44

Android LiOS 8WP8.1

2010-05-25 13:36:16

C#C++

2023-12-07 11:11:01

2012-07-13 16:29:30

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)