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

Java使用MapStruct進行對象映射的流程步驟

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

引言

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

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

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

Java: 17
MapStruct: 1.6.3
Lombok: 1.18.38

1 引入 Maven 依賴

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

我們知道,Lombok 是在編譯期生成代碼的,而 MapStruct 也是在編譯期生成代碼的。所以兩者應該有個先后順序,Lombok 的代碼生成要在 MapStruct 的代碼生成之前執(zhí)行,否則 MapStruct 會在設(shè)置字段時因找不到對應的 Setters 或 Getters 而報錯。因此,除了引入 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ǔ)用法

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

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

下面就是 User VO 的源碼,其擁有 idemail、name、yearOfBirthrolecreatedAt 六個屬性。除了 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 的目標對象 UserDto 的源碼。UserDto 除了和 User 擁有完全相同的字段 idemail 外,其它字段要么是名字相同但類型不同(如 role、createdDate)、要么是類型相同但名字不同(如 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)換做映射。

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

接下來就是字段級的映射配置了:

  • idemail 字段在兩個 POJO 中完全相同,無需配置,MapStruct 會自動幫我們做映射。
  • nameusername 的映射,因名稱發(fā)生了變化,需要使用 @Mapping 注解來指定源字段和目的字段的對應關(guān)系。
  • yearOfBirthage 的映射,因字段意義發(fā)生變化,需要進行計算??赏ㄟ^在 UserMapper 接口定義一個默認方法 calculateAge,然后在 @Mapping 映射中使用 qualifiedByName 指定該實現(xiàn)方法來達成。
  • role 字段在源對象中是枚舉類型,而在目的對象中是 String 類型,我們無需做處理,MapStruct 會自動幫我們將枚舉的 name() 值設(shè)置到目標字段。
  • newCenturyUser 是目標對象的一個新字段,其需基于源對象的 yearOfBirth 字段進行計算,可以通過指定一個表達式(expression)來達成。
  • 源字段的 createdAtDate 類型,目的字段的 createdDateLocalDateTime 類型,可以通過引入一個兩者轉(zhuǎn)換的 Util 類或其它 Mapper 來達成(這里引入了 DateTimeUtil 工具類,且在 @Mapping 映射中未指定任何方法,因為 MapStruct 會自動幫我們檢測適用的方法并調(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 的使用還是比較簡單的:名稱與類型完全相同的字段無需任何額外配置;類型相同但名稱不同的字段則只需指定 source 和 target 即可;需要額外計算的字段,可以通過編寫表達式或引入其它 Mapper 或工具類來實現(xiàn)。

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

最后,我們編寫一個單元測試類來對上述 Mapper 進行一下測試。

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 命令運行如上單元測試后發(fā)現(xiàn)打印的目標對象 UserDto 的字段值與預期一致。

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

3 進階用法

3.1 多個源對象到一個目的對象的映射

若想將多個源對象映射到一個目的對象,@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 嵌套對象和集合對象映射

如果對象之間有嵌套,或者是集合對象,MapStruct 也能很好的勝任對應的轉(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 注解可以自動生成反向映射規(guī)則,避免重復定義。

package com.example.demo.mapper;

@Mapper
public interface UserMapper {

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

    @InheritInverseConfiguration
    User toUser(UserDto userDto);
}

3.4 默認值和常量

還可以為映射提供默認值或常量。下面的示例中:當 Username 字段為 null 時,MapStruct 即會為目標字段賦上默認值 Unknown;此外,還可以為目標對象中 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 進行注入并使用了。

package com.example.demo.mapper;

public class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

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

5 小結(jié)

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

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

相關(guān)文章

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

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

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

    深入解析Java的Servlet過濾器的原理及其應用

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

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

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

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

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

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

    這篇文章主要介紹了Mybatis-plus中的@EnumValue注解使用詳解,在PO類中,如果我們直接使用枚舉類型去映射數(shù)據(jù)庫的對應字段保存時,往往就會因為類型不匹配導致映射失敗,Mybatis-plus提供了一種解決辦法,就是使用@EnumValue注解,需要的朋友可以參考下
    2024-02-02
  • 四個實例超詳細講解Java?貪心和枚舉的特點與使用

    四個實例超詳細講解Java?貪心和枚舉的特點與使用

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

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

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

    詳解Guava Cache本地緩存在Spring Boot應用中的實踐

    Guava Cache是一個全內(nèi)存的本地緩存實現(xiàn),本文將講述如何將 Guava Cache緩存應用到 Spring Boot應用中。具有一定的參考價值,感興趣的小伙伴們可以參考一下
    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)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習,需要的朋友們下面隨著小編來一起學習學習吧。
    2017-10-10

最新評論

娄烦县| 边坝县| 芦山县| 九江市| 林西县| 宣汉县| 阳原县| 吴川市| 松桃| 江永县| 绥化市| 富民县| 巩留县| 门源| 宜丰县| 改则县| 曲水县| 康保县| 龙胜| 五常市| 宣城市| 鄄城县| 黄冈市| 武城县| 抚远县| 辽阳市| 星座| 根河市| 疏附县| 乌什县| 灵川县| 通州市| 彭阳县| 宁化县| 延边| 治多县| 兴化市| 伊宁县| 加查县| 青冈县| 德令哈市|