七種對象複製工具類,阿粉該 Pick 誰?

天天早上七點三十,準時推送乾貨



平常編程中,咱們會常常會碰到對象屬性複製的場景,就好比下面這樣一個常見的三層 MVC 架構。html

當咱們在上面的架構下編程時,咱們一般須要經歷對象轉化,好比業務請求流程經歷三層機構後須要把 DTO 轉爲DO而後在數據庫中保存。java

當須要從數據查詢數據頁面展現時,查詢數據通過三層架構將會從 DO  轉爲 DTO,最後再轉爲 VO,而後在頁面中展現。git

當業務簡單的時候,咱們手寫代碼,經過 getter/setter複製對象屬性,十分簡單。可是一旦業務變的複雜,對象屬性變得不少,那麼手寫代碼就會成爲程序員的噩夢。程序員

不但手寫十分繁瑣,很是耗時間,而且還可能容易出錯。github

阿粉以前就經歷過一個項目,一個對象中大概有四五十個字段屬性,那時候阿粉還剛入門,什麼都不太懂,寫了半天 getter/setter複製對象屬性。web

話外音:一個對象屬性這麼多,顯然是不太合理的,咱們設計過程應該將其拆分。spring

直到後來,阿粉瞭解到了對象屬性複製工具類,使用以後,發現是真香,不再用手寫代碼。再後來,碰到愈來愈多工具類,雖然核心功能都是同樣的,可是仍是存在不少差別。新手看到可能會一臉懵逼,不知道如何選擇。數據庫

因此阿粉今天這篇介紹一下市面上經常使用的工具類:apache

  • Apache BeanUtils
  • Spring BeanUtils
  • Cglib BeanCopier
  • Dozer
  • orika
  • MapStruct

工具類特性

在介紹這些工具類以前,咱們來看下一個好用的屬性複製工具類,須要有哪些特性:編程

  • 基本屬性複製,這個是基本功能
  • 不一樣類型的屬性賦值,好比基本類型與其包裝類型等
  • 不一樣字段名屬性賦值,固然字段名應該儘可能保持一致,可是實際業務中,因爲不一樣開發人員,或者筆誤拼錯單詞,這些緣由均可能致使會字段名不一致的狀況
  • 淺拷貝/深拷貝,淺拷貝會引用同一對象,若是稍微不慎,同時改動對象,就會踩到意想不到的坑

下面咱們開始介紹工具類。

畫外音:公號內回覆「20200822」獲取源碼

Apache BeanUtils

首先介紹是第一位應該是 Java 領域屬性複製的最有名的工具類「Apache BeanUtils」,這個工具類想必不少人或多或少用過或則見過。

沒用過也不要緊,咱們來展現這個類的用法,用法很是簡單。

首先咱們引入依賴,這裏使用最新版本:

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
</dependency>

假設咱們項目中有以下類:

此時咱們須要完成 DTO 對象轉化到 DO 對象,咱們只須要簡單調用BeanUtils#copyProperties 方法就能夠完成對象屬性的複製。

StudentDTO studentDTO = new StudentDTO();
studentDTO.setName("阿粉");
studentDTO.setAge(18);
studentDTO.setNo("6666");

List<String> subjects = new ArrayList<>();
subjects.add("math");
subjects.add("english");
studentDTO.setSubjects(subjects);
studentDTO.setCourse(new Course("CS-1"));
studentDTO.setCreateDate("2020-08-08");

StudentDO studentDO = new StudentDO();

BeanUtils.copyProperties(studentDO, studentDTO);

不過,上面的代碼若是你這麼寫,咱們會碰到第一個問題,BeanUtils 默認不支持 String轉爲 Date 類型。

爲了解決這個問題,咱們須要本身構造一個 Converter 轉換類,而後使用 ConvertUtils註冊,使用方法以下:

ConvertUtils.register(new Converter() {
    @SneakyThrows
    @Override
    public <Date> Date convert(Class<Date> type, Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof String) {
            String str = (String) value;
            return (Date) DateUtils.parseDate(str, "yyyy-MM-dd");
        }
        return null;


    }
}, Date.class);

此時,咱們觀察 studentDOstudentDTO對象屬性值:

從上面的圖咱們能夠得出BeanUtils一些結論:

  • 普通字段名不一致的屬性沒法被複制
  • 嵌套對象字段,將會與源對象使用同一對象,即便用 淺拷貝
  • 類型不一致的字段,將會進行默認類型轉化。

雖然 BeanUtils 使用起來很方便,不過其底層源碼爲了追求完美,加了過多的包裝,使用了不少反射,作了不少校驗,因此致使性能較差,因此並阿里巴巴開發手冊上強制規定避免使用 Apache BeanUtils

image-20200818222213879

Spring BeanUtils

Spring 屬性複製工具類類名與 Apache 同樣,基本用法也差很少。我先來看下 Spring BeanUtils 基本用法。

一樣,咱們先引入依賴,從名字咱們能夠看出,BeanUtils 位於 Spring-Beans 模塊,這裏咱們依然使用最新模塊。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

這裏咱們使用 DTO 與 DO 複用上面的例子,轉換代碼以下:

// 省略上面賦值代碼,與上面一致
StudentDO studentDO = new StudentDO();

BeanUtils.copyProperties(studentDTO, studentDO);

從用法能夠看到,Spring BeanUtils 與 Apache 有一個最大的不一樣,二者源對象與目標對象參數位置不同,阿粉以前沒注意,用了 Spring 工具類,可是倒是按照 Apache 的用法使用。

此時對比studentDOstudentDTO對象:

從上面的對比圖咱們能夠獲得一些結論:

  • 字段名不一致,屬性沒法複製
  • 類型不一致,屬性沒法複製。可是注意,若是類型爲基本類型以及基本類型的包裝類,這種能夠轉化
  • 嵌套對象字段,將會與源對象使用同一對象,即便用 淺拷貝

除了這個方法以外,Spring BeanUtils 還提供了一個重載方法:

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

使用這個方法,咱們能夠忽略某些不想被複制過去的屬性:

BeanUtils.copyProperties(studentDTO, studentDO,"name");

這樣,name 屬性就不會被複制到 DO 對象中。

雖然 Spring BeanUtils 與 Apache BeanUtils 功能差很少,可是在性能上 Spring BeanUtils  仍是完爆 Apache BeanUtils。主要緣由仍是在於 Spring 並無與 Apache 同樣使用反射作了過多校驗,另外 Spring BeanUtils 內部使用了緩存,加快轉換的速度。

因此二者選擇,仍是推薦使用 Spring BeanUtils。

Cglib BeanCopier

上面兩個是阿粉平常工做常用,而下面的這些都是阿粉最近纔開始接觸的,好比 Cglib BeanCopier。這個使用方法,可能比上面兩個類稍微複雜一點,下面咱們來看下具體用法:

首先咱們引入 Cglib 依賴:

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

畫外音:若是你工程內還有 Spring-Core 的話,若是查找 BeanCopier 這個類,能夠發現兩個不一樣的包的同名類。

一個屬於 Cglib,另外一個屬於 Spring-Core。

其實 Spring-Core 內BeanCopier 實際就是引入了 Cglib 中的類,這麼作的目的是爲包了保證 Spring 使用長度 Cglib 相關類的穩定性,防止外部 Cglib 依賴不一致,致使 Spring 運行異常。

轉換代碼以下:

// 省略賦值語句
StudentDO studentDO = new StudentDO();
BeanCopier beanCopier = BeanCopier.create(StudentDTO.class, StudentDO.class, false);
beanCopier.copy(studentDTO, studentDO, null);

使用方法相比 BeanUtils, BeanCopier 稍微多了一步。 對比studentDOstudentDTO對象:

從上面能夠獲得與 Spring Beanutils 基本一致的結論:

  • 字段名不一致,屬性沒法複製
  • 類型不一致,屬性沒法複製。 不過有點不同,若是類型爲基本類型/基本類型的包裝類型,這二者沒法被拷貝。
  • 嵌套對象字段,將會與源對象使用同一對象,即便用 淺拷貝

上面咱們使用 Beanutils,遇到這種字段名,類型不一致的這種狀況,咱們沒有什麼好辦法,只能手寫硬編碼。

不過在 BeanCopier 下,咱們能夠引入轉換器,進行類型轉換。

// 注意最後一個屬性設置爲 true
BeanCopier beanCopier = BeanCopier.create(StudentDTO.class, StudentDO.class, true);
// 自定義轉換器
beanCopier.copy(studentDTO, studentDO, new Converter() {
    @Override
    public Object convert(Object source, Class target, Object context) {
        if (source instanceof Integer) {
            Integer num = (Integer) source;
            return num.toString();
        }
        return null;
    }
});

不過吐槽一下這個轉換器,一旦咱們本身打開使用轉換器,全部屬性複製都須要咱們本身來了。好比上面的例子中,咱們只處理當源對象字段類型爲 Integer,這種狀況,其餘都沒處理。咱們獲得 DO 對象將會只有 name 屬性才能被複制。

Cglib BeanCopier 的原理與上面兩個 Beanutils 原理不太同樣,其主要使用 字節碼技術動態生成一個代理類,代理類實現get 和 set方法。生成代理類過程存在必定開銷,可是一旦生成,咱們能夠緩存起來重複使用,全部 Cglib 性能相比以上兩種 Beanutils 性能比較好。

Dozer

Dozer ,中文直譯爲挖土機 ,這是一個「重量級」屬性複製工具類,相比於上面介紹三個工具類,Dozer 具備不少強大的功能。

官網 logo

畫外音:重量級/輕量級其實只是一個相對的說法,因爲 Dozer 相對 BeanUtils 這類工具類來講,擁有許多高級功能,因此相對來講這是一個重量級工具類。

阿粉剛碰到這個工具類,就被深深折服,真的太強大了,上面咱們指望的功能,Dozer 都給你實現了。

下面咱們來看下使用方法,首先咱們引入 Dozer 依賴:

<dependency>
  <groupId>net.sf.dozer</groupId>
  <artifactId>dozer</artifactId>
  <version>5.4.0</version>
</dependency>

使用方法以下:

// 省略屬性的代碼
DozerBeanMapper mapper = new DozerBeanMapper();
StudentDO studentDO =
        mapper.map(studentDTO, StudentDO.class);
System.out.println(studentDO);

Dozer 須要咱們新建一個DozerBeanMapper,這個類做用等同與 BeanUtils,負責對象之間的映射,屬性複製。

畫外音:下面的代碼咱們能夠看到,生成 DozerBeanMapper實例須要加載配置文件,隨意生成代價比較高。在咱們應用程序中,應該使用單例模式,重複使用DozerBeanMapper

若是屬性都是一些簡單基本類型,那咱們只要使用上面代碼,能夠快速完成屬性複製。

不過很不幸,咱們的代碼中有字符串與 Date 類型轉化,若是咱們直接使用上面的代碼,程序運行將會拋出異常。

因此這裏咱們要用到 Dozer 強大的配置功能,咱們總共可使用下面三種方式:

  • XML
  • API
  • 註解

其中,API 的方式比較繁瑣,目前大部分使用 XML 進行,另外註解功能的是在 Dozer  5.3.2 以後增長的新功能,不過功能相較於 XML 來講較弱。

XML 使用方式

下面咱們使用 XML 配置方式,配置 DTO 與 DO 關係,首先咱們新建一個 dozer/dozer-mapping.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozer.sourceforge.net
          http://dozer.sourceforge.net/schema/beanmapping.xsd"
>

    <!-- 類級別的日期轉換,默認使用這個格式轉換    -->
    <mapping date-format="yyyy-MM-dd HH:mm:ss">
        <class-a>com.just.doone.example.domain.StudentDTO</class-a>
        <class-b>com.just.doone.example.domain.StudentDO</class-b>
        <!-- 在下面指定字段名不一致的映射關係       -->
        <field>
            <a>no</a>
            <b>number</b>
        </field>

        <field>
            <!-- 字段級別的日期轉換,將會覆蓋字段上的轉換            -->
            <a date-format="yy-MM-dd">createDate</a>
            <b>createDate</b>
        </field>
    </mapping>
</mappings>

而後修改咱們的 Java 代碼,增長讀取 Dozer 的配置文件:

DozerBeanMapper mapper = new DozerBeanMapper();
List<String> mappingFiles = new ArrayList<>();
// 讀取配置文件
mappingFiles.add("dozer/dozer-mapping.xml");
mapper.setMappingFiles(mappingFiles);
StudentDO studentDO = mapper.map(studentDTO, StudentDO.class);
System.out.println(studentDO);

運行以後,對比studentDOstudentDTO對象:

從上面的圖咱們能夠發現:

  • 類型不一致的字段,屬性被複制
  • DO 與 DTO 對象字段不是同一個對象,也就是深拷貝
  • 經過配置字段名的映射關係,不同字段的屬性也被複制

除了上述這些相對簡單的屬性之外,Dozer 還支持不少額外的功能,好比枚舉屬性複製,Map 等集合屬性複製等。

有些小夥伴剛看到 Dozer 的用法,可能以爲這個工具類比較繁瑣,不像 BeanUtils 工具類同樣一行代碼就能夠解。

其實 Dozer 能夠很好跟 Spring 框架整合,咱們能夠在 Spring 配置文件提早配置,後續咱們只要引用 Dozer 的相應的 Bean ,使用方式也是一行代碼。

Dozer 與 Spring 整合,咱們可使用其 DozerBeanMapperFactoryBean,配置以下:

    <bean class="org.dozer.spring.DozerBeanMapperFactoryBean">
        <property name="mappingFiles" 
                  value="classpath*:/*mapping.xml"/>

      <!--自定義轉換器-->
        <property name="customConverters">
            <list>
                <bean class=
                      "org.dozer.converters.CustomConverter"/>
      
            </list>
        </property>
    </bean>

DozerBeanMapperFactoryBean支持設置屬性比較多,能夠自定義設置類型轉換,還能夠設置其餘屬性。

另外還有一種簡單的方法,咱們能夠在 XML 中配置 DozerBeanMapper

    <bean id="org.dozer.Mapper" class="org.dozer.DozerBeanMapper">
        <property name="mappingFiles">
            <list>
                <value>dozer/dozer-Mapperpping.xml</value>
            </list>
        </property>
    </bean>

Spring 配置完成以後,咱們在代碼中能夠直接注入:

@Autowired
Mapper mapper;

public void objMapping(StudentDTO studentDTO) {
// 直接使用
StudentDO studentDO =
mapper.map(studentDTO, StudentDO.class);
}

註解方式

Dozer 註解方式相比 XML 配置來講功能很弱,只能完成字段名不一致的映射。

上面的代碼中,咱們能夠在 DTO 的 no 字段上使用  @Mapping 註解,這樣咱們在使用 Dozer 完成轉換時,該字段屬性將會被複制。

@Data
public class StudentDTO {

    private String name;

    private Integer age;
    @Mapping("number")
    private String no;

    private List<String> subjects;

    private Course course;
    private String createDate;
}

雖然目前註解功能有點薄弱,不事後看版本官方可能增長新的註解功能,另外 XML 與註解能夠一塊兒使用。

最後  Dozer 底層本質上仍是使用了反射完成屬性的複製,因此執行速度並非那麼理想。

orika

orika也是一個跟 Dozer 相似的重量級屬性複製工具類,也提供諸如 Dozer 相似的功能。可是 orika 無需使用繁瑣 XML 配置,它自身提供一套很是簡潔的 API 用法,很是容易上手。

首先咱們引入其最新的依賴:

<dependency>
    <groupId>ma.glasnost.orika</groupId>
    <artifactId>orika-core</artifactId>
    <version>1.5.4</version>
</dependency>

基本使用方法以下:

// 省略其餘設值代碼

// 這裏先不要設值時間
// studentDTO.setCreateDate("2020-08-08");

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
MapperFacade mapper = mapperFactory.getMapperFacade();
StudentDO studentDO = mapper.map(studentDTO, StudentDO.class);

這裏咱們引入兩個類 MapperFactoryMapperFacade,其中 MapperFactory 能夠用於字段映射,配置轉換器等,而 MapperFacade 的做用就與 Beanutils 同樣,用於負責對象的之間的映射。

上面的代碼中,咱們故意註釋了 DTO 對象中的 createDate 時間屬性的設值,這是由於默認狀況下若是沒有單獨設置時間類型的轉換器,上面的代碼將會拋錯。

另外,上面的代碼中,對於字段名不一致的屬性,是不會複製的,因此咱們須要單獨設置。

下面咱們就設置一個時間轉換器,而且指定一下字段名:

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
ConverterFactory converterFactory = mapperFactory.getConverterFactory();
converterFactory.registerConverter(new DateToStringConverter("yyyy-MM-dd"));
mapperFactory.classMap(StudentDTO.class, StudentDO.class)
        .field("no""number")
      // 必定要調用下 byDefault
        .byDefault()
        .register();
MapperFacade mapper = mapperFactory.getMapperFacade();
StudentDO studentDO = mapper.map(studentDTO, StudentDO.class);

上面的代碼中,首先咱們須要在 ConverterFactory 註冊一個時間類型的轉換器,其次咱們還須要再 MapperFactory 指定不一樣字段名的之間的映射關係。

這裏咱們要注意,在咱們使用 classMap 以後,若是想要相同字段名屬性默認被複制,那麼必定調用  byDefault方法。

簡單對比一下 DTO 與 DO 對象:

上圖能夠發現 orika 的一些特性:

  • 默認支持類型不一致(基本類型/包裝類型)轉換
  • 支持深拷貝
  • 指定不一樣字段名映射關係,屬性能夠被成功複製。

另外 orika 還支持集合映射:

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
List<Person> persons = new ArrayList<>();
List<PersonDto> personDtos = mapperFactory.getMapperFacade().mapAsList(persons, PersonDto.class);

最後聊下 orika 實現原理,orika 與 dozer 底層原理不太同樣,底層其使用了 javassist 生成字段屬性的映射的字節碼,而後直接動態加載執行字節碼文件,相比於 Dozer 的這種使用反射原來的工具類,速度上會快不少。

MapStruct

不知不覺,一口氣已經寫了 5 個屬性複製工具類,小夥伴都看到這裏,那就不要放棄了,堅持看完,下面將介紹一個與上面這些都不太同樣的工具類「MapStruct」。

上面介紹的這些工具類,無論使用反射,仍是使用字節碼技術,這些都須要在代碼運行期間動態執行,因此相對於手寫硬編碼這種方式,上面這些工具類執行速度都會慢不少。

那有沒有一個工具類的運行速度與硬編碼這種方式差很少那?

這就要介紹 MapStruct 這個工具類,這個工具類之因此運行速度與硬編碼差很少,這是由於他在編譯期間就生成了 Java Bean 屬性複製的代碼,運行期間就無需使用反射或者字節碼技術,因此確保了高性能。

另外,因爲編譯期間就生成了代碼,因此若是有任何問題,編譯期間就能夠提早暴露,這對於開發人員來說就能夠提早解決問題,而不用等到代碼應用上線了,運行以後才發現錯誤。

下面咱們來看下,怎麼使用這個工具類,首先咱們先引入這個依賴:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.3.1.Final</version>
</dependency>

其次,因爲 MapStruct 須要在編譯器期間生成代碼,因此咱們須要 maven-compiler-plugin插件中配置:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
    <configuration>
        <source>1.8</source> <!-- depending on your project -->
        <target>1.8</target> <!-- depending on your project -->
        <annotationProcessorPaths>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>1.3.1.Final</version>
            </path>
            <!-- other annotation processors -->
        </annotationProcessorPaths>
    </configuration>
</plugin>

接下來咱們須要定義映射接口,代碼以下:

@Mapper
public interface StudentMapper {

    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
  
    @Mapping(source = "no", target = "number")
    @Mapping(source = "createDate", target = "createDate", dateFormat = "yyyy-MM-dd")
    StudentDO dtoToDo(StudentDTO studentDTO);
}

咱們須要使用 MapStruct 註解 @Mapper 定義一個轉換接口,這樣定義以後,StudentMapper 的功能就與 BeanUtils 等工具類同樣了。

其次,因爲咱們  DTO 與 DO 對象中存在字段名不一致的狀況,因此咱們還在在轉換方法上使用 @Mapping 註解指定字段映射。另外咱們 createDate 字段類型不一致,這裏咱們還須要指定時間格式化類型。

上面定義完成以後,咱們就能夠直接使用 StudentMapper 一行代碼搞定對象轉換。

// 忽略其餘代碼
StudentDO studentDO = StudentMapper.INSTANCE.dtoToDo(studentDTO);

若是咱們對象使用 Lombok 的話,使用 @Mapping指定不一樣字段名,編譯期間可能會拋出以下的錯誤:

這個緣由主要是由於 Lombok 也須要編譯期間自動生成代碼,這就可能致使二者衝突,當 MapStruct 生成代碼時,還不存在 Lombok 生成的代碼。

解決辦法能夠在 maven-compiler-plugin插件配置中加入 Lombok,以下:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
    <configuration>
        <source>1.8</source> <!-- depending on your project -->
        <target>1.8</target> <!-- depending on your project -->
        <annotationProcessorPaths>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>1.3.1.Final</version>
            </path>
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
            </path>
            <!-- other annotation processors -->
        </annotationProcessorPaths>
    </configuration>
</plugin>

輸出 DO 與 DTO 以下:

從上圖中咱們能夠獲得一些結論:

  • 部分類型不一致,能夠自動轉換,好比
    • 基本類型與包裝類型
    • 基本類型的包裝類型與 String
  • 深拷貝

上面介紹的例子介紹一些簡單字段映射,若是小夥伴在工做總共還碰到其餘的場景,能夠先查看一下這個工程,查看一下有沒有結局解決辦法:https://github.com/mapstruct/mapstruct-examples

上面咱們已經知道 MapStruct 在編譯期間就生成了代碼,下面咱們來看下自動生成代碼:

public class StudentMapperImpl implements StudentMapper {
    public StudentMapperImpl() {
    }

    public StudentDO dtoToDo(StudentDTO studentDTO) {
        if (studentDTO == null) {
            return null;
        } else {
            StudentDO studentDO = new StudentDO();
            studentDO.setNumber(studentDTO.getNo());

            try {
                if (studentDTO.getCreateDate() != null) {
                    studentDO.setCreateDate((new SimpleDateFormat("yyyy-MM-dd")).parse(studentDTO.getCreateDate()));
                }
            } catch (ParseException var4) {
                throw new RuntimeException(var4);
            }

            studentDO.setName(studentDTO.getName());
            if (studentDTO.getAge() != null) {
                studentDO.setAge(String.valueOf(studentDTO.getAge()));
            }

            List<String> list = studentDTO.getSubjects();
            if (list != null) {
                studentDO.setSubjects(new ArrayList(list));
            }

            studentDO.setCourse(studentDTO.getCourse());
            return studentDO;
        }
    }
}

從生成的代碼來看,裏面並無什麼黑魔法,MapStruct 自動生成了一個實現類 StudentMapperImpl,裏面實現了 dtoToDo,方法裏面調用 getter/setter設值。

從這個能夠看出,MapStruct 做用就至關於幫咱們手寫getter/setter設值,因此它的性能會很好。

總結

看文這篇文章,咱們一共學習了 7 個屬性複製工具類,這麼多工具類咱們該如何選擇那?阿粉講講本身的一些看法:

第一,首先咱們直接拋棄 Apache Beanutils ,這個不用說了,阿里巴巴規範都這樣定了,咱們就不要使用好了。

第二,固然是看工具類的性能,這些工具類的性能,網上文章介紹的比較多,阿粉就複製過來,你們能夠比較一下。

來自:https://www.hollischuang.com/archives/5337
來自:https://www.baeldung.com/java-performance-mapping-frameworks

能夠看到 MapStruct 的性能能夠說仍是至關優秀。那麼若是你的業務對於性能,響應等要求比較高,或者你的業務存在大數據量導入/導出的場景,而這個代碼存在對象轉化,那就切勿使用 Apache Beanutils, Dozer 這兩個工具類。

第三,其實很大一部分應用是沒有很高的性能的要求,只要工具類能提供足夠的便利,就能夠接受。若是你的業務中沒有很複雜的的需求,那麼直接使用 Spring Beanutils 就行了,畢竟 Spring 的包大部分應用都在使用,咱們都無需導入其餘包了。

那麼若是業務存在不一樣類型,不一樣的字段名,那麼能夠考慮使用  orika 等這種重量級工具類。

好了,今天的文章就到這裏爲止了~

參考

  1. https://www.cnkirito.moe/orika/
  2. https://www.hollischuang.com/archives/5337
  3. http://dozer.sourceforge.net/documentation/usage.html
  4. http://orika-mapper.github.io/orika-docs/faq.html
  5. https://github.com/mapstruct/mapstruct-examples


< END >

若是你們喜歡咱們的文章,歡迎你們轉發,點擊在看讓更多的人看到。也歡迎你們熱愛技術和學習的朋友加入的咱們的知識星球當中,咱們共同成長,進步。



往期 精彩回顧




秒殺真那麼難?手把手帶你優化秒殺流程
手把手教你搭建一個單一機器的Hadoop,你要看一下麼?
你肯定 LinkedList 在新增/刪除元素時,效率比 ArrayList 高?


本文分享自微信公衆號 - Java極客技術(Javageektech)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索