偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

推薦一個(gè)對(duì)象轉(zhuǎn)換神器:MapStruct

開發(fā) 開發(fā)工具
MapStruct在編譯時(shí)生成具體的映射實(shí)現(xiàn)代碼,運(yùn)行時(shí)直接調(diào)用這些方法,避免了反射開銷。而其他工具需要在運(yùn)行時(shí)通過(guò)反射動(dòng)態(tài)解析和執(zhí)行映射,性能自然差很多。

前言

今天我要給大家安利一個(gè)讓我相見恨晚的開發(fā)神器——MapStruct。

相信不少小伙伴在工作中都寫過(guò)這樣的"搬磚"代碼:

// 傳統(tǒng)的DTO轉(zhuǎn)換 - 又臭又長(zhǎng)的setter方法
UserDTO userDTO = new UserDTO();
userDTO.setId(userEntity.getId());
userDTO.setName(userEntity.getName());
userDTO.setEmail(userEntity.getEmail());
userDTO.setCreateTime(userEntity.getCreateTime());
userDTO.setUpdateTime(userEntity.getUpdateTime());
// ...還有十幾個(gè)字段要設(shè)置,寫到手抽筋!

這種手動(dòng)轉(zhuǎn)換的方式不僅繁瑣易錯(cuò),而且維護(hù)起來(lái)特別痛苦。

今天我就帶大家徹底解決這個(gè)問(wèn)題!

一、為什么我們需要對(duì)象轉(zhuǎn)換工具?

1.1 現(xiàn)實(shí)開發(fā)中的分層架構(gòu)

在現(xiàn)代應(yīng)用開發(fā)中,我們通常采用分層架構(gòu),不同層使用不同的對(duì)象模型:

圖片圖片

每層之間的數(shù)據(jù)傳遞都需要進(jìn)行對(duì)象轉(zhuǎn)換,這就產(chǎn)生了大量的轉(zhuǎn)換代碼。

1.2 傳統(tǒng)轉(zhuǎn)換方式的三大痛點(diǎn)

1. 手動(dòng)setter方式(最原始但性能最好):

// 優(yōu)點(diǎn):性能好
// 缺點(diǎn):代碼冗長(zhǎng),容易出錯(cuò),難以維護(hù)
UserResponse response = new UserResponse();
response.setId(user.getId());
response.setUserName(user.getName());
response.setUserEmail(user.getEmail());
// ...省略無(wú)數(shù)行setter

2. Apache BeanUtils(使用反射,性能較差):

import org.apache.commons.beanutils.BeanUtils;

// 優(yōu)點(diǎn):代碼簡(jiǎn)潔
// 缺點(diǎn):性能差,類型轉(zhuǎn)換容易出錯(cuò)
UserDTO dto = new UserDTO();
try {
    BeanUtils.copyProperties(user, dto);
} catch (Exception e) {
    log.error("屬性拷貝失敗", e);
}

3. Spring BeanUtils(比Apache好點(diǎn),但仍有問(wèn)題):

import org.springframework.beans.BeanUtils;

// 優(yōu)點(diǎn):比Apache性能稍好
// 缺點(diǎn):仍然使用反射,復(fù)雜映射支持有限
UserDTO dto = new UserDTO();
BeanUtils.copyProperties(user, dto);

二、MapStruct:編譯生成的性能王者

2.1 什么是MapStruct?

MapStruct是一個(gè)基于注解處理器在編譯時(shí)生成映射代碼的工具。它與眾不同的地方在于:

  • 編譯時(shí)生成:在編譯期間生成具體的映射實(shí)現(xiàn)類,運(yùn)行時(shí)無(wú)反射開銷
  • 類型安全:編譯期間檢查映射是否正確,提前發(fā)現(xiàn)錯(cuò)誤
  • 易于調(diào)試:生成的是普通Java代碼,可以輕松調(diào)試
  • 功能強(qiáng)大:支持復(fù)雜映射、自定義轉(zhuǎn)換、集合映射等

2.2 快速開始:5分鐘上手MapStruct

1. 添加Maven依賴:

<dependencies>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>1.5.3.Final</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>1.5.3.Final</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

2. 創(chuàng)建實(shí)體類和DTO:

// 實(shí)體類
public class User {
    private Long id;
    private String username;
    private String email;
    private Date createTime;
    private Integer status;
    
    // getters and setters
}

// DTO類
public class UserDTO {
    private Long id;
    private String name;
    private String email;
    private String createTime;
    private String statusDesc;
    
    // getters and setters
}

3. 創(chuàng)建映射接口:

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface UserMapper {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
    
    @Mapping(source = "username", target = "name")
    @Mapping(source = "createTime", target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
    @Mapping(target = "statusDesc", expression = "java(mapStatus(user.getStatus()))")
    UserDTO userToUserDTO(User user);
    
    default String mapStatus(Integer status) {
        if (status == null) {
            return"未知";
        }
        switch (status) {
            case1: return"激活";
            case2: return"禁用";
            default: return"未知";
        }
    }
}

4. 使用映射器:

// 編譯時(shí)MapStruct會(huì)自動(dòng)生成UserMapperImpl實(shí)現(xiàn)類
User user = userRepository.findById(1L);
UserDTO userDTO = UserMapper.INSTANCE.userToUserDTO(user);

三、MapStruct核心功能詳解

3.1 基本映射:字段名自動(dòng)匹配

當(dāng)字段名相同時(shí),MapStruct會(huì)自動(dòng)映射:

import org.mapstruct.Mapper;

@Mapper
public interface SimpleMapper {
    // 自動(dòng)映射相同字段名
    UserDTO userToUserDTO(User user);
    
    // 反向映射
    User userDTOToUser(UserDTO userDTO);
}

3.2 字段名不同的映射

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;

@Mapper
public interface UserMapper {
    
    @Mappings({
        @Mapping(source = "username", target = "name"),
        @Mapping(source = "createTime", target = "registerTime")
    })
    UserDTO userToUserDTO(User user);
}

3.3 類型轉(zhuǎn)換和格式化

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import java.math.BigDecimal;
import java.text.DecimalFormat;

@Mapper
public interface ProductMapper {
    
    @Mapping(source = "price", target = "price", numberFormat = "$#.00")
    @Mapping(source = "weight", target = "weightText")
    ProductDTO productToProductDTO(Product product);
    
    default String mapWeight(BigDecimal weight) {
        if (weight == null) {
            return"0.0kg";
        }
        return weight.setScale(2, BigDecimal.ROUND_HALF_UP) + "kg";
    }
}

3.4 多對(duì)象聚合映射

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;

@Mapper
public interface OrderMapper {
    
    @Mappings({
        @Mapping(source = "user.id", target = "userId"),
        @Mapping(source = "user.name", target = "userName"),
        @Mapping(source = "order.orderNo", target = "orderNumber"),
        @Mapping(source = "order.amount", target = "orderAmount"),
        @Mapping(source = "address.city", target = "city"),
        @Mapping(source = "address.detail", target = "addressDetail")
    })
    OrderDTO aggregateToDTO(User user, Order order, Address address);
}

四、高級(jí)特性:讓轉(zhuǎn)換更智能

4.1 集合映射

import org.mapstruct.Mapper;
import java.util.List;
import java.util.Set;

@Mapper
public interface CollectionMapper {
    
    // 列表映射
    List<UserDTO> usersToUserDTOs(List<User> users);
    
    // 集合映射
    Set<UserDTO> usersToUserDTOs(Set<User> users);
    
    // 自動(dòng)使用單個(gè)對(duì)象映射方法
    List<UserDTO> mapUserList(List<User> users);
}

4.2 嵌套對(duì)象映射

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

public class User {
    private Long id;
    private String name;
    private Department department; // 嵌套對(duì)象
}

public class Department {
    private Long id;
    private String name;
    private Company company; // 多層嵌套
}

public class UserDTO {
    private Long id;
    private String userName;
    private String departmentName;
    private String companyName;
}

@Mapper
public interface NestedMapper {
    
    @Mapping(source = "name", target = "userName")
    @Mapping(source = "department.name", target = "departmentName")
    @Mapping(source = "department.company.name", target = "companyName")
    UserDTO userToUserDTO(User user);
}

4.3 使用組件模型(Spring集成)

import org.mapstruct.Mapper;
import org.springframework.stereotype.Component;

@Mapper(componentModel = "spring") // 生成Spring組件
@Component
public interface UserMapper {
    
    UserDTO userToUserDTO(User user);
}

// 在Service中注入使用
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper; // 自動(dòng)注入
    
    public UserDTO getUserById(Long id) {
        User user = userRepository.findById(id);
        return userMapper.userToUserDTO(user);
    }
}

4.4 自定義映射方法

import org.mapstruct.Mapper;
import org.mapstruct.Named;
import java.util.Base64;

@Mapper
public interface CustomMapper {
    
    @Mapping(source = "password", target = "passwordHash", qualifiedByName = "hashPassword")
    UserDTO userToUserDTO(User user);
    
    @Named("hashPassword")
    default String hashPassword(String password) {
        if (password == null) {
            returnnull;
        }
        return Base64.getEncoder().encodeToString(password.getBytes());
    }
    
    // 多個(gè)自定義方法
    @Mapping(source = "rawData", target = "processedData", qualifiedByName = "processData")
    @Mapping(source = "timestamp", target = "formattedTime", qualifiedByName = "formatTime")
    DataDTO mapData(Data data);
    
    @Named("processData")
    default String processData(String rawData) {
        return"Processed: " + rawData;
    }
    
    @Named("formatTime")
    default String formatTime(Long timestamp) {
        return new java.util.Date(timestamp).toString();
    }
}

五、性能對(duì)比

5.1 性能測(cè)試對(duì)比

讓我們用真實(shí)數(shù)據(jù)對(duì)比各種對(duì)象轉(zhuǎn)換工具的性能:

import org.openjdk.jmh.annotations.*;
import org.springframework.beans.BeanUtils;
import org.apache.commons.beanutils.BeanUtils;
import org.modelmapper.ModelMapper;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Thread)
public class MappingBenchmark {
    
    private static final User user = createTestUser();
    private static final UserMapper mapStructMapper = Mappers.getMapper(UserMapper.class);
    private static final ModelMapper modelMapper = new ModelMapper();
    
    @Benchmark
    public void testManualMapping() {
        // 手動(dòng)setter方式
        UserDTO dto = new UserDTO();
        dto.setId(user.getId());
        dto.setName(user.getName());
        dto.setEmail(user.getEmail());
        // ...15個(gè)字段
    }
    
    @Benchmark
    public void testMapStruct() {
        mapStructMapper.userToUserDTO(user);
    }
    
    @Benchmark
    public void testSpringBeanUtils() {
        UserDTO dto = new UserDTO();
        BeanUtils.copyProperties(user, dto);
    }
    
    @Benchmark
    public void testApacheBeanUtils() {
        try {
            UserDTO dto = new UserDTO();
            BeanUtils.copyProperties(user, dto);
        } catch (Exception e) {
            // ignore
        }
    }
    
    @Benchmark
    public void testModelMapper() {
        UserDTO dto = modelMapper.map(user, UserDTO.class);
    }
}

性能測(cè)試結(jié)果(ops/s,越大越好):

轉(zhuǎn)換方式

平均性能

相對(duì)性能

內(nèi)存占用

手動(dòng)setter

1,200,000 ops/s

100%

MapStruct

1,150,000 ops/s

95.8%

Spring BeanUtils

350,000 ops/s

29.2%

Apache BeanUtils

50,000 ops/s

4.2%

ModelMapper

280,000 ops/s

23.3%

5.2 為什么MapStruct性能這么好?

圖片圖片

MapStruct在編譯時(shí)生成具體的映射實(shí)現(xiàn)代碼,運(yùn)行時(shí)直接調(diào)用這些方法,避免了反射開銷。

而其他工具需要在運(yùn)行時(shí)通過(guò)反射動(dòng)態(tài)解析和執(zhí)行映射,性能自然差很多。

六、最佳實(shí)踐

6.1 全局配置和共享設(shè)置

import org.mapstruct.MapperConfig;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.Mapping;

@MapperConfig(
    componentModel = "spring",
    unmappedTargetPolicy = ReportingPolicy.ERROR, // 嚴(yán)格模式:未映射字段報(bào)錯(cuò)
    unmappedSourcePolicy = ReportingPolicy.WARN,  // 源字段未映射警告
    collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED
)
public interface CentralConfig {
    // 全局日期格式配置
    @Mapping(target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
    @Mapping(target = "updateTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
    void configureTimestamps(Object source, Object target);
}

// 繼承全局配置
@Mapper(config = CentralConfig.class)
public interface UserMapper {
    // 具體映射方法
    UserDTO userToUserDTO(User user);
}

6.2 單元測(cè)試和調(diào)試

生成的代碼位置:

target/generated-sources/annotations/com/example/mapper/UserMapperImpl.java

單元測(cè)試示例:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class UserMapperTest {
    
    @Test
    public void testUserToUserDTOMapping() {
        // 準(zhǔn)備測(cè)試數(shù)據(jù)
        User user = new User();
        user.setId(1L);
        user.setUsername("蘇三");
        user.setEmail("susan@example.com");
        user.setCreateTime(new Date());
        user.setStatus(1);
        
        // 執(zhí)行轉(zhuǎn)換
        UserDTO dto = UserMapper.INSTANCE.userToUserDTO(user);
        
        // 驗(yàn)證結(jié)果
        assertNotNull(dto);
        assertEquals(user.getId(), dto.getId());
        assertEquals(user.getUsername(), dto.getName());
        assertEquals(user.getEmail(), dto.getEmail());
        assertNotNull(dto.getCreateTime());
        assertEquals("激活", dto.getStatusDesc());
    }
    
    @Test
    public void testNullSafeMapping() {
        // 測(cè)試空對(duì)象安全
        User user = null;
        UserDTO dto = UserMapper.INSTANCE.userToUserDTO(user);
        assertNull(dto);
    }
}

七、常見問(wèn)題解決方案

7.1 編譯問(wèn)題和配置

問(wèn)題:MapStruct注解處理器不工作解決方案:

<!-- 確保Maven配置正確 -->
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <source>11</source>
                <target>11</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>1.5.3.Final</version>
                    </path>
                    <!-- 如果有Lombok,需要同時(shí)配置 -->
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>1.18.24</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

7.2 復(fù)雜映射場(chǎng)景處理

場(chǎng)景:需要根據(jù)條件動(dòng)態(tài)映射字段解決方案:

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.condition.Condition;

@Mapper
public interface ConditionalMapper {
    
    @Mapping(target = "displayName", source = "username", 
             condition = "notBlank")
    @Mapping(target = "profileUrl", source = "id",
             condition = "positiveId")
    UserDTO userToUserDTO(User user);
    
    @Condition
    default boolean notBlank(String value) {
        return value != null && !value.trim().isEmpty();
    }
    
    @Condition
    default boolean positiveId(Long id) {
        return id != null && id > 0;
    }
}

總結(jié)

經(jīng)過(guò)全面的介紹和對(duì)比,我們來(lái)總結(jié)一下MapStruct的核心價(jià)值:

8.1 MapStruct的五大優(yōu)勢(shì)

  1. 性能卓越:編譯時(shí)生成代碼,無(wú)運(yùn)行時(shí)反射開銷,性能接近手動(dòng)setter
  2. 類型安全:編譯時(shí)檢查映射正確性,提前發(fā)現(xiàn)錯(cuò)誤
  3. 功能豐富:支持復(fù)雜映射、自定義轉(zhuǎn)換、集合映射等高級(jí)特性
  4. 易于調(diào)試:生成的是普通Java代碼,可以輕松調(diào)試和優(yōu)化
  5. 集成簡(jiǎn)單:支持Spring、CDI等主流框架,無(wú)縫集成現(xiàn)有項(xiàng)目

8.2 適用場(chǎng)景

  • 分層架構(gòu)開發(fā):DTO、VO、BO、Entity之間的轉(zhuǎn)換
  • API接口開發(fā):請(qǐng)求/響應(yīng)對(duì)象的轉(zhuǎn)換和格式化
  • 微服務(wù)架構(gòu):服務(wù)間數(shù)據(jù)傳輸對(duì)象的轉(zhuǎn)換
  • 數(shù)據(jù)導(dǎo)出功能:領(lǐng)域?qū)ο蟮綄?dǎo)出數(shù)據(jù)的轉(zhuǎn)換
  • 老舊系統(tǒng)改造:替換原有的反射式轉(zhuǎn)換工具

8.3 不適用場(chǎng)景

  • 極度簡(jiǎn)單的映射:只有2-3個(gè)字段需要轉(zhuǎn)換
  • 動(dòng)態(tài)映射需求:運(yùn)行時(shí)才能確定映射規(guī)則的場(chǎng)景
  • 無(wú)編譯環(huán)境:無(wú)法使用注解處理器的特殊環(huán)境

好的工具不僅要解決當(dāng)前問(wèn)題,更要為未來(lái)的維護(hù)和擴(kuò)展考慮。

MapStruct不僅提升了開發(fā)效率,更重要的是它讓代碼更加健壯、可維護(hù)和可測(cè)試。

責(zé)任編輯:武曉燕 來(lái)源: 蘇三說(shuō)技術(shù)
相關(guān)推薦

2022-09-02 08:17:40

MapStruct代碼工具

2025-08-27 08:24:23

2023-05-14 23:38:43

Glarity用戶視頻

2022-09-28 10:35:31

JavaScript代碼內(nèi)存泄漏

2022-05-27 09:02:31

Openbase開源前端

2025-07-26 00:00:00

AI驅(qū)動(dòng)工具

2023-11-22 08:26:03

HutoolJava工具集

2018-08-15 15:23:48

視頻

2022-02-06 20:55:39

jsEsbuild項(xiàng)目

2024-04-07 00:00:01

TypeScript語(yǔ)言REST

2021-08-19 09:00:12

監(jiān)控文件Python

2019-02-25 10:18:43

工具代碼測(cè)試

2025-05-09 08:40:00

WAF開源網(wǎng)站防護(hù)墻

2015-07-03 11:27:30

程序員自己神器

2025-01-09 06:00:00

Checkmate監(jiān)控系統(tǒng)開源

2025-03-07 08:31:54

2021-10-14 18:15:38

BeanUtils對(duì)象生成器

2022-05-12 09:17:06

SQLPython

2021-04-25 08:58:00

Go拍照云盤

2023-08-28 08:19:05

點(diǎn)贊
收藏

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