李锋镝的博客

  • 首页
  • 时间轴
  • 评论区显眼包🔥
  • 左邻右舍
  • 博友圈
  • 关于我
    • 关于我
    • 另一个网站
    • 我的导航站
    • 网站地图
    • 赞助
  • 留言
  • 🚇开往
Destiny
自是人生长恨水长东
  1. 首页
  2. 后端
  3. 正文

MapStruct深度解析:从原理到实战,告别BeanUtil的性能与安全痛点

2025年10月22日 247点热度 0人点赞 0条评论

在Java开发中,对象映射是高频操作——从DTO转实体、实体转VO,再到多数据源合并为目标对象,几乎每个业务层都离不开。传统的BeanUtil.copyProperties虽简单,却存在反射开销大、类型不安全、字段映射不可控等问题。而MapStruct作为编译时生成映射代码的框架,完美解决了这些痛点,成为企业级项目的首选方案。本文将从核心原理、核心注解、实战案例到常见问题,全方位拆解MapStruct的使用。

一、MapStruct核心认知:为什么它比BeanUtil更优?

在学习使用前,首先要明确MapStruct的本质与优势,理解它为何能替代传统映射工具。

1.1 什么是MapStruct?

MapStruct是一款Java注解驱动的代码生成器,专注于简化Java Bean之间的映射实现。它通过在编译期分析映射接口与注解,自动生成纯Java代码的映射实现类,而非运行时通过反射完成映射。

简单来说:MapStruct不做“运行时魔法”,而是在项目编译时就帮你写好userToUserDTO这类映射方法,你直接调用即可。

1.2 核心优势:对比BeanUtil的碾压性优势

传统工具(如Apache BeanUtils、Spring BeanUtils)与MapStruct的差异,本质是“反射”与“编译期代码生成”的差异,具体体现在4个维度:

对比维度 传统BeanUtil(反射实现) MapStruct(编译期生成代码)
性能 反射需动态解析类结构,开销大(比MapStruct慢10-100倍) 纯Java代码调用(get/set),无额外开销
类型安全 运行时才会发现类型不匹配(如String转Integer) 编译期报错,提前规避风险
灵活性 仅支持字段名完全匹配,自定义映射需额外代码 支持字段别名、类型转换、多数据源映射等
可调试性 反射逻辑黑盒,无法断点调试 生成的代码清晰可读,可直接断点排查

示例对比:同样是User转UserDTO,MapStruct生成的代码如下(编译后可见):

// MapStruct自动生成的实现类
public class UserMapperImpl implements UserMapper {
    @Override
    public UserDTO userToUserDTO(User user) {
        if (user == null) {
            return null;
        }
        UserDTO userDTO = new UserDTO();
        userDTO.setId(user.getId()); // 直接调用get/set
        userDTO.setUsername(user.getUsername());
        userDTO.setEmail(user.getEmail());
        userDTO.setCreateTime(user.getCreateTime().toString()); // 若配置了类型转换
        userDTO.setAddressDTO(addressToAddressDTO(user.getAddress())); // 关联对象映射
        return userDTO;
    }
}

这种纯Java代码的映射方式,性能与手写代码完全一致,且无需担心反射带来的问题。

1.3 工作原理:编译期生成代码的3个步骤

MapStruct的核心是“注解处理器(Annotation Processor)”,它在Maven/Gradle编译项目时触发,完成3个关键步骤:

  1. 解析注解:扫描项目中带@Mapper注解的接口,分析接口中的映射方法(如userToUserDTO)、@Mapping等注解配置;
  2. 生成映射代码:根据注解配置,生成接口的实现类(如UserMapperImpl),实现类中包含字段映射、类型转换、关联对象处理等逻辑;
  3. 编译加载:生成的实现类与项目其他代码一起编译为class文件,运行时可通过接口直接调用(支持Spring依赖注入)。

二、环境搭建:5分钟集成MapStruct

MapStruct的集成需依赖核心包与注解处理器,支持Maven和Gradle,以下以Maven为例。

2.1 Maven依赖配置

需在pom.xml中添加3部分配置:核心依赖、注解处理器、Lombok兼容(若项目使用Lombok)。

<properties>
    <!-- MapStruct版本(建议使用1.6.x及以上,兼容Java 8+) -->
    <org.mapstruct.version>1.6.3</org.mapstruct.version>
</properties>

<dependencies>
    <!-- 1. MapStruct核心依赖(接口与基础类) -->
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <!-- 2. Maven编译插件:配置注解处理器 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source> <!-- 项目Java版本 -->
                <target>1.8</target>
                <annotationProcessorPaths>
                    <!-- 3. MapStruct注解处理器(关键:编译时生成代码) -->
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                    <!-- 4. Lombok兼容:若项目用Lombok,需添加此绑定包 -->
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok-mapstruct-binding</artifactId>
                        <version>0.2.0</version>
                    </path>
                    <!-- 5. Lombok注解处理器(若项目用Lombok) -->
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>1.18.30</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

2.2 关键配置说明

  • 注解处理器路径:annotationProcessorPaths必须包含mapstruct-processor,否则无法生成映射代码;
  • Lombok兼容:Lombok 1.18.16+与MapStruct存在编译顺序冲突,需添加lombok-mapstruct-binding协调两者的注解处理顺序;
  • Java版本:MapStruct 1.6.x支持Java 8-17,若使用Java 11+,需确保maven-compiler-plugin版本≥3.8.1。

三、核心注解:掌握90%场景的关键配置

MapStruct通过注解定义映射规则,核心注解仅6个,覆盖字段映射、类型转换、生命周期等场景。

3.1 @Mapper:标记映射接口(入口注解)

@Mapper是MapStruct的核心注解,用于标记“映射接口/抽象类”,告诉注解处理器需要为该接口生成实现类。

常用属性:

  • componentModel:指定映射器的组件模型,控制实现类的实例化方式(如Spring依赖注入);
    • default:默认值,通过Mappers.getMapper(Class)获取实例(无依赖注入);
    • spring:生成@Component修饰的实现类,支持@Autowired注入;
    • cdi:支持CDI容器(Java EE);
  • uses:指定当前映射器依赖的其他映射器(如UserMapper依赖AddressMapper);
  • unmappedTargetPolicy:未映射目标字段的处理策略(如ReportingPolicy.ERROR表示未映射时编译报错)。

示例:Spring环境下的映射接口

// Spring环境:生成@Component,可@Autowired注入
@Mapper(componentModel = "spring", uses = AddressMapper.class)
public interface UserMapper {
    // 映射方法:User → UserDTO
    UserDTO userToUserDTO(User user);

    // 映射方法:UserDTO → User
    User userDTOToUser(UserDTO userDTO);

    // 集合映射:List<User> → List<UserDTO>(无需手动实现)
    List<UserDTO> userListToUserDTOList(List<User> userList);
}

3.2 @Mapping:字段级映射规则

当源对象与目标对象的字段名不匹配、类型不同,或需要自定义映射逻辑时,用@Mapping注解配置单个字段的规则。

常用属性:

  • source:源对象的字段名(如user.getCreateTime());
  • target:目标对象的字段名(如userDTO.setCreateDate());
  • dateFormat:日期类型转换格式(如"yyyy-MM-dd HH:mm:ss",适用于Date/String互转);
  • numberFormat:数字类型转换格式(如"#.00",适用于BigDecimal/String互转);
  • qualifiedByName:指定自定义转换方法(配合@Named注解);
  • expression:通过Java表达式自定义映射逻辑(如拼接字符串);
  • defaultValue:源字段为null时的默认值(如"未知");
  • constant:直接给目标字段设置常量(如"USER")。

示例:多场景字段映射

@Mapper(componentModel = "spring")
public interface UserMapper {
    // 1. 字段名不匹配:source="createTime" → target="createDate"
    // 2. 日期格式转换:LocalDateTime → String(格式:yyyy-MM-dd HH:mm:ss)
    // 3. 源字段为null时,target默认值为"未知用户"
    @Mapping(source = "createTime", target = "createDate", dateFormat = "yyyy-MM-dd HH:mm:ss")
    @Mapping(source = "username", target = "nickname", defaultValue = "未知用户")
    @Mapping(target = "userType", constant = "COMMON") // 常量赋值
    @Mapping(target = "fullName", expression = "java(source.getFirstName() + \" \" + source.getLastName())") // 表达式拼接
    UserDTO userToUserDTO(User user);
}

3.3 @MappingTarget:更新已有对象(非新建)

默认情况下,MapStruct会新建目标对象(如new UserDTO()),若需“更新已有对象”(如从DTO更新实体的部分字段),用@MappingTarget标记目标对象参数。

示例:更新用户信息

@Mapper(componentModel = "spring")
public interface UserMapper {
    /**
     * 用UserUpdateDTO更新User对象(非新建User)
     * @param dto 源对象(更新数据来源)
     * @param user 目标对象(待更新的已有对象)
     */
    @Mapping(source = "email", target = "email") // 仅更新email字段
    @Mapping(source = "phone", target = "phone") // 仅更新phone字段
    void updateUserFromDTO(UserUpdateDTO dto, @MappingTarget User user);
}

// 调用方式(Service层)
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public void updateUser(Long userId, UserUpdateDTO dto) {
        User user = userRepository.findById(userId).orElseThrow();
        userMapper.updateUserFromDTO(dto, user); // 直接更新已有user对象
        userRepository.save(user);
    }
}

3.4 @ValueMapping:枚举类型映射

当源枚举与目标枚举的常量名不匹配时(如PaymentStatus.PENDING → PaymentStatusDTO.INIT),用@ValueMapping配置枚举映射规则。

示例:枚举映射

// 源枚举(数据库实体用)
public enum PaymentStatus {
    PENDING,  // 待支付
    PROCESSING, // 处理中
    COMPLETED, // 已完成
    FAILED // 失败
}

// 目标枚举(DTO用)
public enum PaymentStatusDTO {
    INIT,     // 初始化(对应PENDING)
    IN_PROGRESS, // 处理中(对应PROCESSING)
    SUCCESS,  // 成功(对应COMPLETED)
    ERROR     // 错误(对应FAILED)
}

// 枚举映射器
@Mapper(componentModel = "spring")
public interface PaymentStatusMapper {
    @ValueMappings({
        @ValueMapping(source = "PENDING", target = "INIT"),
        @ValueMapping(source = "PROCESSING", target = "IN_PROGRESS"),
        @ValueMapping(source = "COMPLETED", target = "SUCCESS"),
        @ValueMapping(source = "FAILED", target = "ERROR"),
        // 源为null时,默认映射为INIT
        @ValueMapping(source = MappingConstants.NULL, target = "INIT"),
        // 未匹配的其他枚举值,默认映射为ERROR
        @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "ERROR")
    })
    PaymentStatusDTO toDTO(PaymentStatus status);
}

3.5 @BeforeMapping/@AfterMapping:映射生命周期

在映射开始前(@BeforeMapping)或结束后(@AfterMapping)执行自定义逻辑,如参数校验、字段补全(如lastUpdateTime)。

示例:映射前后的校验与补全

@Mapper(componentModel = "spring")
public abstract class UserMapper { // 注意:用抽象类而非接口,支持自定义方法
    // 映射前校验:确保源对象非空、邮箱非空
    @BeforeMapping
    protected void validateUserDTO(UserDTO source) {
        if (source == null) {
            throw new IllegalArgumentException("UserDTO cannot be null");
        }
        if (source.getEmail() == null || source.getEmail().isEmpty()) {
            throw new IllegalArgumentException("Email is required");
        }
    }

    // 映射后补全:自动设置更新时间和版本号
    @AfterMapping
    protected void enrichUserEntity(@MappingTarget User target) {
        target.setLastUpdateTime(LocalDateTime.now());
        target.setVersion(target.getVersion() + 1); // 乐观锁版本号自增
    }

    // 抽象映射方法(MapStruct会生成实现)
    @Mapping(source = "userId", target = "id")
    public abstract User toEntity(UserDTO source);
}

3.6 @BeanMapping:对象级映射配置

@BeanMapping用于配置“整个对象”的映射规则(而非单个字段),如null值处理策略、是否忽略默认映射等。

常用属性:

  • nullValueMappingStrategy:源对象为null时的策略(如RETURN_DEFAULT返回目标对象的默认实例);
  • nullValuePropertyMappingStrategy:源字段为null时的策略(如IGNORE不更新目标字段);
  • ignoreByDefault:是否默认忽略所有字段映射(需手动配置@Mapping才生效);
  • ignoreUnmappedSourceProperties:忽略源对象中未映射的字段(避免编译警告)。

示例:忽略null值更新

@Mapper(componentModel = "spring")
public interface AddressMapper {
    /**
     * 更新地址:源字段为null时,不更新目标字段(保留原有值)
     */
    @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
    @Mapping(source = "street", target = "street")
    @Mapping(source = "city", target = "city")
    void updateAddress(AddressDTO dto, @MappingTarget Address address);
}

四、实战案例:覆盖80%业务场景

结合实际业务场景,演示MapStruct在“基础映射、类型转换、多数据源合并、集合映射”等场景的使用。

4.1 场景1:基础DTO-实体映射(含关联对象)

需求:将User实体(含Address关联对象)转换为UserDTO(含AddressDTO关联对象),字段名部分不匹配。

步骤1:定义实体与DTO

// 1. 关联实体:Address
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Address {
    private Long id;
    private String street; // 街道(如"123 Main St")
    private String city;   // 城市(如"Beijing")
    private String postalCode; // 邮编(如"100000")
}

// 2. 主实体:User
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String username; // 用户名
    private String email;    // 邮箱
    private LocalDateTime createTime; // 创建时间
    private Address address; // 关联地址
}

// 3. 关联DTO:AddressDTO
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AddressDTO {
    private Long id;
    private String street;
    private String city;
    private String postalCode;
}

// 4. 主DTO:UserDTO
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO {
    private Long id;
    private String nickname; // 对应User.username(字段名不匹配)
    private String email;
    private String createDate; // 对应User.createTime(类型不匹配:LocalDateTime→String)
    private AddressDTO addressDTO; // 对应User.address(关联对象映射)
}

步骤2:定义映射器

// 1. 关联对象映射器:AddressMapper
@Mapper(componentModel = "spring")
public interface AddressMapper {
    AddressDTO addressToDTO(Address address);
    Address dtoToAddress(AddressDTO dto);
}

// 2. 主映射器:UserMapper(依赖AddressMapper)
@Mapper(
    componentModel = "spring",
    uses = AddressMapper.class, // 依赖AddressMapper处理关联对象
    unmappedTargetPolicy = ReportingPolicy.IGNORE // 忽略未映射的字段(避免警告)
)
public interface UserMapper {
    @Mapping(source = "username", target = "nickname") // 字段名不匹配
    @Mapping(source = "createTime", target = "createDate", dateFormat = "yyyy-MM-dd HH:mm:ss") // 日期类型转换
    @Mapping(source = "address", target = "addressDTO") // 关联对象映射(依赖AddressMapper)
    UserDTO userToDTO(User user);

    // 反向映射:DTO→实体
    @Mapping(source = "nickname", target = "username")
    @Mapping(source = "createDate", target = "createTime", dateFormat = "yyyy-MM-dd HH:mm:ss")
    @Mapping(source = "addressDTO", target = "address")
    User dtoToUser(UserDTO dto);
}

步骤3:调用映射器(Service层)

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserRepository userRepository;

    // 获取用户详情(实体→DTO)
    public UserDTO getUserDetail(Long userId) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new RuntimeException("用户不存在"));
        return userMapper.userToDTO(user); // 直接调用映射方法
    }

    // 创建用户(DTO→实体)
    public void createUser(UserDTO userDTO) {
        User user = userMapper.dtoToUser(userDTO);
        user.setCreateTime(LocalDateTime.now()); // 补充默认值
        userRepository.save(user);
    }
}

4.2 场景2:多数据源合并映射

需求:将UserBasicInfo(基础信息)和UserExtInfo(扩展信息)两个对象,合并为UserDetailVO(详情VO)。

步骤1:定义数据源与目标VO

// 数据源1:基础信息
@Data
public class UserBasicInfo {
    private Long userId;
    private String username;
    private String email;
}

// 数据源2:扩展信息
@Data
public class UserExtInfo {
    private Long userId;
    private Integer age;
    private String phone;
    private String address;
}

// 目标VO:用户详情
@Data
public class UserDetailVO {
    private Long id;
    private String username;
    private String email;
    private Integer age;
    private String phone;
    private String address;
}

步骤2:定义多数据源映射器

@Mapper(componentModel = "spring")
public interface UserDetailMapper {
    /**
     * 多数据源合并:BasicInfo + ExtInfo → DetailVO
     * 注:当多个数据源有同名字段(如userId),需用source指定数据源参数名
     */
    @Mapping(source = "basic.userId", target = "id") // 明确指定从basic获取userId
    @Mapping(source = "basic.username", target = "username")
    @Mapping(source = "basic.email", target = "email")
    @Mapping(source = "ext.age", target = "age") // 从ext获取age
    @Mapping(source = "ext.phone", target = "phone")
    @Mapping(source = "ext.address", target = "address")
    UserDetailVO mergeToDetailVO(UserBasicInfo basic, UserExtInfo ext);
}

步骤3:调用合并映射

@Service
public class UserDetailService {
    @Autowired
    private UserBasicMapper basicMapper; // 假设从DB查询基础信息
    @Autowired
    private UserExtMapper extMapper;     // 假设从Redis查询扩展信息
    @Autowired
    private UserDetailMapper detailMapper;

    public UserDetailVO getUserDetail(Long userId) {
        // 1. 获取两个数据源
        UserBasicInfo basic = basicMapper.getByUserId(userId);
        UserExtInfo ext = extMapper.getByUserId(userId);
        // 2. 合并为VO
        return detailMapper.mergeToDetailVO(basic, ext);
    }
}

4.3 场景3:自定义类型转换(如String转枚举)

需求:将前端传入的字符串状态(如"A")转换为后端枚举(如UserStatus.ACTIVE),并映射到User实体。

步骤1:定义枚举与DTO

// 枚举:用户状态
public enum UserStatus {
    ACTIVE("A", "激活"),
    INACTIVE("I", "未激活"),
    LOCKED("L", "锁定");

    private final String code;
    private final String desc;

    // 构造器、getter省略
    public static UserStatus getByCode(String code) {
        for (UserStatus status : values()) {
            if (status.code.equals(code)) {
                return status;
            }
        }
        throw new IllegalArgumentException("无效状态码:" + code);
    }
}

// DTO:前端传入的创建参数
@Data
public class UserCreateDTO {
    private String username;
    private String email;
    private String statusCode; // 前端传入状态码(如"A")
}

// 实体:User
@Data
public class User {
    private Long id;
    private String username;
    private String email;
    private UserStatus status; // 后端枚举
}

步骤2:自定义转换方法+映射器

@Mapper(componentModel = "spring")
public interface UserCreateMapper {
    // 1. 自定义转换方法:String(code)→ UserStatus
    default UserStatus stringToUserStatus(String code) {
        if (code == null || code.isEmpty()) {
            return UserStatus.INACTIVE; // 默认未激活
        }
        return UserStatus.getByCode(code);
    }

    // 2. 映射方法:DTO→实体(使用自定义转换)
    @Mapping(source = "statusCode", target = "status", qualifiedByName = "stringToUserStatus")
    @Named("stringToUserStatus") // 与qualifiedByName对应
    User toEntity(UserCreateDTO dto);
}

4.4 场景4:集合映射(List/Set映射)

需求:将List<User>转换为List<UserDTO>,MapStruct自动支持集合映射,无需手动循环。

示例:集合映射器

@Mapper(componentModel = "spring", uses = AddressMapper.class)
public interface UserListMapper {
    // 1. 单个对象映射(基础)
    @Mapping(source = "username", target = "nickname")
    @Mapping(source = "createTime", target = "createDate", dateFormat = "yyyy-MM-dd")
    UserDTO userToDTO(User user);

    // 2. List映射(MapStruct自动生成循环逻辑)
    List<UserDTO> userListToDTOList(List<User> userList);

    // 3. Set映射(同理)
    Set<UserDTO> userSetToDTOSet(Set<User> userSet);
}

// 调用示例
@Service
public class UserListService {
    @Autowired
    private UserListMapper listMapper;
    @Autowired
    private UserRepository userRepository;

    public List<UserDTO> getUserList() {
        List<User> userList = userRepository.findAll();
        return listMapper.userListToDTOList(userList); // 直接映射集合
    }
}

五、常见问题与解决方案

在使用MapStruct时,常会遇到编译报错、Lombok兼容、代码不生成等问题,以下是高频问题的解决方案。

5.1 问题1:映射代码未生成(编译后无Impl类)

原因:

  1. 未配置mapstruct-processor注解处理器;
  2. Maven编译时未触发注解处理器(如用IDE直接运行,未执行mvn compile);
  3. 映射接口未加@Mapper注解。

解决方案:

  1. 检查pom.xml的annotationProcessorPaths是否包含mapstruct-processor;
  2. 执行mvn clean compile手动触发编译,生成Impl类;
  3. 确保映射接口添加了@Mapper注解。

5.2 问题2:Lombok与MapStruct编译冲突(字段无法识别)

现象:使用Lombok的@Data注解时,MapStruct无法识别实体的get/set方法,编译报错“找不到字段的setter”。

原因:Lombok与MapStruct的注解处理器执行顺序冲突,MapStruct先处理时,Lombok尚未生成get/set方法。

解决方案:

  1. 添加lombok-mapstruct-binding依赖(已在环境搭建中配置);
  2. 确保annotationProcessorPaths中,lombok在前,mapstruct-processor在后;
  3. 若仍有问题,在IDE中开启“注解处理”(IntelliJ IDEA:Settings → Build → Compiler → Annotation Processors → 勾选“Enable annotation processing”)。

5.3 问题3:未映射字段编译警告/报错

现象:编译时提示“Unmapped target property: 'xxx'”(未映射目标字段)。

原因:

  1. 目标对象的字段在源对象中无对应字段,且未配置忽略;
  2. unmappedTargetPolicy设置为ReportingPolicy.ERROR(默认是WARNING)。

解决方案:

  1. 若字段无需映射,在@Mapper中配置unmappedTargetPolicy = ReportingPolicy.IGNORE;
  2. 若仅需忽略部分字段,在@BeanMapping中用ignoreUnmappedSourceProperties指定;
  3. 若确实遗漏映射,补充@Mapping注解。

5.4 问题4:类型转换失败(如LocalDateTime转String)

现象:编译报错“Can't map property 'java.time.LocalDateTime createTime' to 'java.lang.String createDate'”。

原因:MapStruct默认不支持LocalDateTime与String的转换,需手动配置格式。

解决方案:

  1. 使用dateFormat属性指定转换格式(适用于Date/LocalDateTime与String互转);
  2. 自定义转换方法(如LocalDateTime.toString())。
// 示例:LocalDateTime→String转换
@Mapping(source = "createTime", target = "createDate", dateFormat = "yyyy-MM-dd HH:mm:ss")
UserDTO userToDTO(User user);

六、总结:MapStruct的最佳实践

  1. 优先使用componentModel = "spring":在Spring项目中,通过依赖注入使用映射器,避免手动获取实例;
  2. 明确字段映射规则:即使字段名匹配,建议关键字段显式配置@Mapping,提高代码可读性;
  3. 拆分复杂映射:多数据源合并、复杂类型转换等场景,拆分为多个小映射器(如AddressMapper、UserMapper),通过uses依赖;
  4. 开启编译期校验:配置unmappedTargetPolicy = ReportingPolicy.ERROR,提前发现未映射字段;
  5. 配合IDE插件:安装IntelliJ IDEA的“MapStruct Support”插件(Plugins → 搜索MapStruct),支持注解补全、代码跳转。
除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接

本文链接:https://www.lifengdi.com/hou-duan/4534

相关文章

  • 配置Jackson使用字段而不是getter/setter来序列化和反序列化
  • JDK25模块级导入深度解析:Java导入机制的革命性进化
  • 数据库更新如何实现乐观锁
  • try...catch性能深度剖析:从JVM原理到实战优化,打破技术迷思
  • Spring WebFlux深度解析:异步非阻塞架构与实战落地指南
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
标签: BeanUtils JAVA MapStruct
最后更新:2025年10月22日

李锋镝

既然选择了远方,便只顾风雨兼程。

打赏 点赞
< 上一篇
下一篇 >

文章评论

1 2 3 4 5 6 7 8 9 11 12 13 14 15 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 46 47 48 49 50 51 52 53 54 55 57 58 60 61 62 63 64 65 66 67 69 72 74 76 77 78 79 80 81 82 85 86 87 90 92 93 94 95 96 97 98 99
取消回复

东风夜放花千树。更吹落、星如雨。宝马雕车香满路。凤箫声动,玉壶光转,一夜鱼龙舞。
蛾儿雪柳黄金缕。笑语盈盈暗香去。众里寻他千百度。蓦然回首,那人却在,灯火阑珊处。

那年今日(05月15日)

  • 1948年:以色列和阿拉伯国家之间的第一次中东战争爆发
  • 1889年:法国埃菲尔铁塔于世界博览会上正式对外开放
  • 1859年:法国物理学家皮埃尔·居里出生
  • 1773年:奥国外交家克莱门斯·梅特涅出生
  • 1567年:意大利作曲家蒙台威尔第出生
  • 更多历史事件
最新 热点 随机
最新 热点 随机
SchedulingConfigurer详解 踩坑60+次后,我终于搞懂 Claude Skill 怎么写才会真的触发 Everything Claude Code 详细使用文档 配置Jackson使用字段而不是getter/setter来序列化和反序列化 这个域名注册整整十年了,十年时间,真快啊 Claude Code全维度实战指南:从入门到精通,解锁AI编程新范式
AI时代,个人技术博客的出路在哪里?这个域名注册整整十年了,十年时间,真快啊WordPress实现用户评论等级排行榜插件WordPress网站换了个字体,差点儿把样式换崩了做了一个WordPress文章热力图插件千万级大表新增字段实战指南:告别锁表与业务中断
TIOBE 12月榜单:C#有望摘得年度语言,R语言重返Top 10 看病难~取药难~~ 醒醒~补个税了 从SQL规范性检查、表结构索引检查着手分析如何优化SQL 企业级自动化 Agent 架构深析:Prompt 演进驱动的智能工作流落地 redis异常记录
标签聚合
日常 分布式 JAVA 架构 数据库 SQL MySQL JVM WordPress AI IDEA AI编程 docker K8s Spring 多线程 Redis SpringBoot 设计模式 ElasticSearch
友情链接
  • Blogs·CN
  • Honesty
  • Mr.Sun的博客
  • 临窗旋墨
  • 哥斯拉
  • 彬红茶日记
  • 志文工作室
  • 懋和道人
  • 拾趣博客导航
  • 搬砖日记
  • 旧时繁华
  • 林羽凡
  • 瓦匠个人小站
  • 皮皮社
  • 知向前端
  • 蜗牛工作室
  • 韩小韩博客
  • 风渡言

COPYRIGHT © 2026 lifengdi.com. ALL RIGHTS RESERVED.

域名年龄

Theme Kratos Made By Dylan

津ICP备2024022503号-3

京公网安备11011502039375号