最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java使用MapStruct進(jìn)行對(duì)象映射的流程步驟

 更新時(shí)間:2025年08月29日 09:18:53   作者:磊磊落落  
MapStruct 是一款基于注解的、用于 Java 對(duì)象映射的代碼生成器,本文即以常見的基于 Maven 管理的 Java 項(xiàng)目為基礎(chǔ),以實(shí)際項(xiàng)目中的 VO(值對(duì)象)到 DTO(數(shù)據(jù)傳輸對(duì)象)的轉(zhuǎn)換為例來演示 MapStruct 的常用功能和使用方式,需要的朋友可以參考下

引言

MapStruct 是一款基于注解的、用于 Java 對(duì)象映射的代碼生成器。借助 MapStruct,我們做對(duì)象轉(zhuǎn)換時(shí),只需按照約定指定映射關(guān)系,真正的逐字段映射交給 MapStruct 去做即可,可以省去大量手工代碼的編寫。而且,MapStruct 是在編譯期生成映射代碼,若有字段類型不一致的映射,會(huì)提前報(bào)錯(cuò),其生成的代碼更加安全可靠。再者,MapStruct 生成的代碼的執(zhí)行性能與我們手工編寫的代碼無異,遠(yuǎn)優(yōu)于市面上流行的幾款基于反射的映射框架(如 BeanUtils、ModelMapper 等)。

本文即以常見的基于 Maven 管理的 Java 項(xiàng)目為基礎(chǔ),以實(shí)際項(xiàng)目中的 VO(值對(duì)象)到 DTO(數(shù)據(jù)傳輸對(duì)象)的轉(zhuǎn)換為例來演示 MapStruct 的常用功能和使用方式。

寫作本文時(shí),用到的 Java、MapStruct、Lombok 的版本如下:

Java: 17
MapStruct: 1.6.3
Lombok: 1.18.38

1 引入 Maven 依賴

開始前,需要在 pom.xml 引入相應(yīng)的依賴。本示例工程為了省去編寫 POJO 類冗長(zhǎng)的 Setters 和 Getters,使用了 Lombok。

我們知道,Lombok 是在編譯期生成代碼的,而 MapStruct 也是在編譯期生成代碼的。所以兩者應(yīng)該有個(gè)先后順序,Lombok 的代碼生成要在 MapStruct 的代碼生成之前執(zhí)行,否則 MapStruct 會(huì)在設(shè)置字段時(shí)因找不到對(duì)應(yīng)的 Setters 或 Getters 而報(bào)錯(cuò)。因此,除了引入 Lombok 和 MapStruct 依賴外,我們還在 maven-compiler-pluginannotationProcessorPaths 部分指定了兩者的處理順序。

<properties>
    <java.version>17</java.version>
    <lombok.version>1.18.38</lombok.version>
    <mapstruct.version>1.6.3</mapstruct.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${mapstruct.version}</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.14.0</version>
            <configuration>
                <source>${java.version}</source>
                <target>${java.version}</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>${lombok.version}</version>
                    </path>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok-mapstruct-binding</artifactId>
                        <version>0.2.0</version>
                    </path>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

2 基礎(chǔ)用法

依賴引入好后,下面就可以開始對(duì) MapStruct 進(jìn)行使用了。本小節(jié)先看一下 MapStruct 的基礎(chǔ)用法,下個(gè)小節(jié)再探索其高級(jí)功能。

我們實(shí)現(xiàn)的場(chǎng)景是 VO 到 DTO 的轉(zhuǎn)換。

下面就是 User VO 的源碼,其擁有 idemail、name、yearOfBirth、rolecreatedAt 六個(gè)屬性。除了 role 為枚舉類型外,其它均為基礎(chǔ)類型。

package com.example.demo.vo;

@Data
public class User {

    private Long id;
    private String email;
    private String name;
    private Integer yearOfBirth;
    private Role role;
    private Date createdAt;
}
package com.example.demo.vo;

public enum Role {
    NORMAL,
    ADMIN
}

下面是 User VO 的目標(biāo)對(duì)象 UserDto 的源碼。UserDto 除了和 User 擁有完全相同的字段 idemail 外,其它字段要么是名字相同但類型不同(如 rolecreatedDate)、要么是類型相同但名字不同(如 username)、要么是全新的字段(如 age、newCenturyUser)。

package com.example.demo.dto;

@Data
public class UserDto {

    private Long id;
    private String email;
    private String username;
    private Integer age;
    private Boolean newCenturyUser;
    private String role;
    private LocalDateTime createdDate;
}

下面即使用 MapStruct 來為 UserUserDto 的轉(zhuǎn)換做映射。

可以看到,我們需要定義一個(gè)專門做轉(zhuǎn)換的接口 UserMapper,并在其上配置 @Mapper 注解,然后在接口中定義一個(gè) UserUserDto 的轉(zhuǎn)換方法 toUserDto()。

接下來就是字段級(jí)的映射配置了:

  • idemail 字段在兩個(gè) POJO 中完全相同,無需配置,MapStruct 會(huì)自動(dòng)幫我們做映射。
  • nameusername 的映射,因名稱發(fā)生了變化,需要使用 @Mapping 注解來指定源字段和目的字段的對(duì)應(yīng)關(guān)系。
  • yearOfBirthage 的映射,因字段意義發(fā)生變化,需要進(jìn)行計(jì)算??赏ㄟ^在 UserMapper 接口定義一個(gè)默認(rèn)方法 calculateAge,然后在 @Mapping 映射中使用 qualifiedByName 指定該實(shí)現(xiàn)方法來達(dá)成。
  • role 字段在源對(duì)象中是枚舉類型,而在目的對(duì)象中是 String 類型,我們無需做處理,MapStruct 會(huì)自動(dòng)幫我們將枚舉的 name() 值設(shè)置到目標(biāo)字段。
  • newCenturyUser 是目標(biāo)對(duì)象的一個(gè)新字段,其需基于源對(duì)象的 yearOfBirth 字段進(jìn)行計(jì)算,可以通過指定一個(gè)表達(dá)式(expression)來達(dá)成。
  • 源字段的 createdAtDate 類型,目的字段的 createdDateLocalDateTime 類型,可以通過引入一個(gè)兩者轉(zhuǎn)換的 Util 類或其它 Mapper 來達(dá)成(這里引入了 DateTimeUtil 工具類,且在 @Mapping 映射中未指定任何方法,因?yàn)?MapStruct 會(huì)自動(dòng)幫我們檢測(cè)適用的方法并調(diào)用)。
package com.example.demo.mapper;

@Mapper(uses = DateTimeUtil.class)
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(source = "name", target = "username")
    @Mapping(source = "yearOfBirth", target = "age", qualifiedByName = "calculateAge")
    @Mapping(target = "newCenturyUser", expression = "java(user.getYearOfBirth() >= 2000)")
    @Mapping(source = "createdAt", target = "createdDate")
    UserDto toUserDto(User user);

    @Named("calculateAge")
    default Integer calculateAge(Integer yearOfBirth) {
        Calendar calendar = Calendar.getInstance();
        return calendar.get(Calendar.YEAR) - yearOfBirth;
    }
}

下面即是 DateTimeUtil 工具類的源碼:

package com.example.demo.util;

public class DateTimeUtil {

    public LocalDateTime asLocalDateTime(Date date) {
        if (null == date) {
            return null;
        }

        return date.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();
    }
}

可以看到,MapStruct 的使用還是比較簡(jiǎn)單的:名稱與類型完全相同的字段無需任何額外配置;類型相同但名稱不同的字段則只需指定 source 和 target 即可;需要額外計(jì)算的字段,可以通過編寫表達(dá)式或引入其它 Mapper 或工具類來實(shí)現(xiàn)。

使用 mvn clean package 命令將工程編譯后會(huì)發(fā)現(xiàn) MapStruct 在 target/classes 目錄對(duì)應(yīng)位置根據(jù)我們的要求自動(dòng)生成了 UserMapper 的實(shí)現(xiàn)類 UserMapperImpl

最后,我們編寫一個(gè)單元測(cè)試類來對(duì)上述 Mapper 進(jìn)行一下測(cè)試。

package com.example.demo.mapper;

public class UserMapperTest {

    @Test
    public void testToUserDto() {
        User user = new User();
        user.setId(1L);
        user.setEmail("larry@larry.com");
        user.setName("Larry");
        user.setYearOfBirth(2000);
        user.setRole(Role.NORMAL);
        user.setCreatedAt(new Date());

        UserDto userDto = UserMapper.INSTANCE.toUserDto(user);
        System.out.println(userDto);
    }
}

執(zhí)行 mvn clean test 命令運(yùn)行如上單元測(cè)試后發(fā)現(xiàn)打印的目標(biāo)對(duì)象 UserDto 的字段值與預(yù)期一致。

UserDto(id=1, email=larry@larry.com, username=Larry, age=25, newCenturyUser=true, role=NORMAL, createdDate=2025-08-28T08:30:10.568)

3 進(jìn)階用法

3.1 多個(gè)源對(duì)象到一個(gè)目的對(duì)象的映射

若想將多個(gè)源對(duì)象映射到一個(gè)目的對(duì)象,@Mapping 配置也很直觀。

package com.example.demo.mapper;

@Mapper
public interface CustomerMapper {

    @Mapping(source = "customer.name", target = "name")
    @Mapping(source = "customer.yearOfBirth", target = "yearOfBirth")
    @Mapping(source = "address.province", target = "province")
    @Mapping(source = "address.city", target = "city")
    @Mapping(source = "address.street", target = "street")
    CustomerDto toCustomerDto(Customer customer, Address address);
}

3.2 嵌套對(duì)象和集合對(duì)象映射

如果對(duì)象之間有嵌套,或者是集合對(duì)象,MapStruct 也能很好的勝任對(duì)應(yīng)的轉(zhuǎn)換。

package com.example.demo.vo;

@Data
public class School {

    private String name;
    private List<Student> students;
}
package com.example.demo.dto;

@Data
public class SchoolDto {

    private String name;
    private List<StudentDto> students;
}
package com.example.demo.mapper;

@Mapper
public interface SchoolMapper {

    SchoolDto toSchoolDto(School school);

    List<SchoolDto> toSchoolDtos(List<School> schools);
}

3.3 逆向映射

若已定義過 UserUserDto 的映射,現(xiàn)在想做 UserDtoUser 的轉(zhuǎn)換怎么辦?使用 @InheritInverseConfiguration 注解可以自動(dòng)生成反向映射規(guī)則,避免重復(fù)定義。

package com.example.demo.mapper;

@Mapper
public interface UserMapper {

    // @Mapping(xxx)
    UserDto toUserDto(User user);

    @InheritInverseConfiguration
    User toUser(UserDto userDto);
}

3.4 默認(rèn)值和常量

還可以為映射提供默認(rèn)值或常量。下面的示例中:當(dāng) Username 字段為 null 時(shí),MapStruct 即會(huì)為目標(biāo)字段賦上默認(rèn)值 Unknown;此外,還可以為目標(biāo)對(duì)象中 String 類型的 level 字段賦上常量值 PRIMARY

package com.example.demo.mapper;

@Mapper
public interface UserMapper {

    @Mapping(source = "name", target = "username", defaultValue = "Unknown")
    @Mapping(target = "level", constant = "PRIMARY")
    UserDto toUserDto(User user);
}

4 如何與 Spring 框架集成?

欲將 MapStruct 與 Spring 集成的話,只需在 Mapper 的 @Mapper 注解上加上 componentModel = "spring" 即可。

package com.example.demo.mapper;

@Mapper(componentModel = "spring")
public interface UserMapper {
}

這樣,即可取代上面的 UserMapper.INSTANCE 的方式,而直接使用 @Autowired 注解將 UserMapper 進(jìn)行注入并使用了。

package com.example.demo.mapper;

public class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testToUserDto() {
        // ...
        UserDto userDto = userMapper.toUserDto(user);
    }
}

5 小結(jié)

綜上,本文首先對(duì) MapStruct 工具進(jìn)行了介紹,然后創(chuàng)建了一個(gè) Maven 示例工程,并將 MapStruct 和 Lombok 依賴引入;然后以實(shí)際項(xiàng)目中的 VO 到 DTO 轉(zhuǎn)換為例,介紹了 MapStruct 的基礎(chǔ)功能和高級(jí)功能;最后還介紹了 MapStruct 與 Spring 框架的集成。總體上感覺 MapStruct 還是比較易用且高效的。

以上就是Java使用MapStruct進(jìn)行對(duì)象映射的流程步驟的詳細(xì)內(nèi)容,更多關(guān)于Java MapStruct對(duì)象映射的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 深入解析JVM之內(nèi)存結(jié)構(gòu)及字符串常量池(推薦)

    深入解析JVM之內(nèi)存結(jié)構(gòu)及字符串常量池(推薦)

    Java作為一種平臺(tái)無關(guān)性的語言,其主要依靠于Java虛擬機(jī)——JVM,接下來通過本文給大家介紹JVM之內(nèi)存結(jié)構(gòu)及字符串常量池的相關(guān)知識(shí),需要的朋友可以參考下
    2020-07-07
  • 深入解析Java的Servlet過濾器的原理及其應(yīng)用

    深入解析Java的Servlet過濾器的原理及其應(yīng)用

    這篇文章主要介紹了深入解析Java的Servlet過濾器的原理及應(yīng)用,Java編寫的Servlet通常是一個(gè)與網(wǎng)頁一起作用于瀏覽器客戶端的程序,需要的朋友可以參考下
    2016-01-01
  • Java使用BigDecimal解決精度丟失問題

    Java使用BigDecimal解決精度丟失問題

    本文詳細(xì)解釋了浮點(diǎn)型數(shù)據(jù)因二進(jìn)制和十進(jìn)制不兼容而導(dǎo)致的精度丟失問題,并指出BigDecimal是解決該問題的有效方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下
    2026-04-04
  • Java 直接插入排序的三種實(shí)現(xiàn)

    Java 直接插入排序的三種實(shí)現(xiàn)

    本文主要介紹了Java 直接插入排序的三種實(shí)現(xiàn)方法,具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-02-02
  • Mybatis-plus中的@EnumValue注解使用詳解

    Mybatis-plus中的@EnumValue注解使用詳解

    這篇文章主要介紹了Mybatis-plus中的@EnumValue注解使用詳解,在PO類中,如果我們直接使用枚舉類型去映射數(shù)據(jù)庫的對(duì)應(yīng)字段保存時(shí),往往就會(huì)因?yàn)轭愋筒黄ヅ鋵?dǎo)致映射失敗,Mybatis-plus提供了一種解決辦法,就是使用@EnumValue注解,需要的朋友可以參考下
    2024-02-02
  • 四個(gè)實(shí)例超詳細(xì)講解Java?貪心和枚舉的特點(diǎn)與使用

    四個(gè)實(shí)例超詳細(xì)講解Java?貪心和枚舉的特點(diǎn)與使用

    貪心算法是指,在對(duì)問題求解時(shí),總是做出在當(dāng)前看來是最好的選擇。也就是說,不從整體最優(yōu)上加以考慮,他所做出的是在某種意義上的局部最優(yōu)解,枚舉法的本質(zhì)就是從所有候選答案中去搜索正確的解,枚舉算法簡(jiǎn)單粗暴,他暴力的枚舉所有可能,盡可能地嘗試所有的方法
    2022-04-04
  • maven中的maven-antrun-plugin插件示例詳解

    maven中的maven-antrun-plugin插件示例詳解

    maven-antrun-plugin?是?Maven?生態(tài)中一個(gè)強(qiáng)大的工具,尤其適合需要復(fù)用?Ant?腳本或?qū)崿F(xiàn)復(fù)雜構(gòu)建邏輯的場(chǎng)景,然而,過度使用可能導(dǎo)致構(gòu)建腳本復(fù)雜化,建議權(quán)衡利弊后合理使用,這篇文章主要介紹了maven中的maven-antrun-plugin插件詳解,需要的朋友可以參考下
    2025-06-06
  • 詳解Guava Cache本地緩存在Spring Boot應(yīng)用中的實(shí)踐

    詳解Guava Cache本地緩存在Spring Boot應(yīng)用中的實(shí)踐

    Guava Cache是一個(gè)全內(nèi)存的本地緩存實(shí)現(xiàn),本文將講述如何將 Guava Cache緩存應(yīng)用到 Spring Boot應(yīng)用中。具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • java中for循環(huán)執(zhí)行的順序圖文詳析

    java中for循環(huán)執(zhí)行的順序圖文詳析

    關(guān)于java的for循環(huán)想必大家非常熟悉,它是java常用的語句之一,這篇文章主要給大家介紹了關(guān)于java中for循環(huán)執(zhí)行順序的相關(guān)資料,需要的朋友可以參考下
    2021-06-06
  • 利用Java快速查找21位花朵數(shù)示例代碼

    利用Java快速查找21位花朵數(shù)示例代碼

    這篇文章主要給大家介紹了關(guān)于利用Java快速查找21位花朵數(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-10-10

最新評(píng)論

珠海市| 社会| 陇西县| 广安市| 德昌县| 乌鲁木齐市| 永善县| 建始县| 汉寿县| 吉水县| 安岳县| 巴林右旗| 西乌珠穆沁旗| 泗洪县| 巴东县| 昌乐县| 琼海市| 楚雄市| 兰溪市| 周宁县| 通城县| 广安市| 安宁市| 资中县| 丹寨县| 彭山县| 南昌市| 普洱| 张家口市| 新乐市| 云阳县| 容城县| 军事| 扶余县| 剑河县| 上饶县| 思茅市| 五台县| 绍兴县| 老河口市| 饶平县|