使用MapStruct映射对象属性

Java教程 2025-09-20

摘要

本文介绍Mapstruct的作用、特点、常用注解、注意事项及坑点解决方案,并用代码示例如何集成到SpringBoot项目中使用。

认识MapStruct

MapStruct适用于所有需要对象映射的场景,如接口的DTO转换、数据库实体与业务对象转换。  

官网: mapstruct.org/  

特点: 

  • 对比BeanUtils的更优选择
  • 支持String转Date等自定义转换
  • 编译时生成代码、高性能、类型安全
  • 约定大于配置,映射时默认按字段名匹配
  • 支持复杂映射,适合对象、列表、分页转换

常用注解: 

  • @Mapper定义接口 
  • @Mappings包含多个@Mappering 
  • @Mappering定义属性映射 
  • 使用@Mappings注解,字段名相同时可不显式指定;defaultValue表示源对象字段不存在时,指定目标字段默认值;ignore表示目标对象字段不需要从源对象中映射过来时,对其忽略;defaultExpression表示源对象中没有对应的属性或源属性值为null时,目标属性将使用这个默认值

注意: 

  • 应该预编译(clean-install)生成实现类 
  • MapStruct生成的实现类位于target目录下 
  • 确保IDEA安装Lombok插件并开启了注解处理器
  • 集成lombok使用时应注意注解冲突(正文有解决方案)

代码示例

1)导入依赖

<dependencies>
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <version>1.18.20version>
    dependency>
    <dependency>
        <groupId>org.mapstructgroupId>
        <artifactId>mapstructartifactId>
        <version>1.5.5.Finalversion>
    dependency>
    <dependency>
        <groupId>org.mapstructgroupId>
        <artifactId>mapstruct-processorartifactId>
        <version>1.5.5.Finalversion>
    dependency>
    <dependency>
        <groupId>org.mapstructgroupId>
        <artifactId>mapstruct-jdk8artifactId>
        <version>1.5.5.Finalversion>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
    dependency>
dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.pluginsgroupId>
            <artifactId>maven-compiler-pluginartifactId>
            <version>3.8.1version>
            <configuration>
                <source>1.8source>
                <target>1.8target>
                <annotationProcessorPaths>
                    
                    <path>
                        <groupId>org.projectlombokgroupId>
                        <artifactId>lombokartifactId>
                        <version>1.18.30version>
                    path>
                    
                    <path>
                        <groupId>org.mapstructgroupId>
                        <artifactId>mapstruct-processorartifactId>
                        <version>1.5.5.Finalversion>
                    path>
                annotationProcessorPaths>
            configuration>
        plugin>
    plugins>
build>

2)原始对象

package org.coffeebeans.entity;

import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;

/**
 * 
  • ClassName: User
  •  *
  • Author: OakWang
  •  */
    @Data @Builder public class User {     private Integer id;     private String username;     private Integer age;     private String address;     private BigDecimal money; }

    3)目标对象

    package org.coffeebeans.entity;
    
    import lombok.Data;
    import java.math.BigDecimal;
    
    /**
     * 
  • ClassName: UserDto
  •  *
  • Author: OakWang
  •  */
    @Data public class UserDto {     private Integer id;     private String name;     private Integer age;     private String familyAddress;     private BigDecimal money; }

    4)定义属性映射接口

    package org.coffeebeans.transform;
    
    import org.coffeebeans.entity.User;
    import org.coffeebeans.entity.UserDto;
    import org.mapstruct.Mapper;
    import org.mapstruct.Mapping;
    import org.mapstruct.Mappings;
    import org.mapstruct.factory.Mappers;
    import java.util.List;
    
    /**
     * 
  • ClassName: UserTransform
  •  *
  • Author: OakWang
  •  */ @Mapper(componentModel = "spring") //生成Spring Bean public interface UserTransform {     UserTransform INSTANCE = Mappers.getMapper(UserTransform.class);     /*     方式1:使用 @Mappings 注解        字段名相同时可不显式指定        defaultValue 源对象字段不存在,指定默认值        ignore 目标对象中存在一些字段不需要从源对象中映射过来时,可以使用ignore属性来忽略这些字段。        defaultExpression 源对象中没有对应的属性或源属性值为null时,目标属性将使用这个默认值      */     //@Mapping(source = "id", target = "id") //同名的可写可不写     @Mapping(source = "username", target = "name")     @Mapping(source = "age", target = "age", defaultValue = "100")     @Mapping(source = "address", target = "familyAddress", ignore = true)     @Mapping(source = "money", target = "money", defaultExpression = "java(new java.math.BigDecimal("3000"))")     UserDto toUserDTO(User user);     //方式2:使用 @Mappings({}) //  @Mappings({ //     @Mapping(source = "username", target = "name"), //     @Mapping(source = "age", target = "age", defaultValue = "0"), //     @Mapping(source = "address", target = "familyAddress", ignore = true), //     @Mapping(source = "money", target = "money", defaultExpression = "java(new BigDecimal("0")") //  }) //  UserDto toUserDTO(User user);     List toUserDtoList(List users);     //todo 一样的用法     //Page toUserDtoPage(Page users); }

    5)编译生成类clean-install

    图片

    package org.coffeebeans.transform;
    
    import java.util.ArrayList;
    import java.util.List;
    import javax.annotation.Generated;
    import org.coffeebeans.entity.User;
    import org.coffeebeans.entity.UserDto;
    import org.springframework.stereotype.Component;
    
    @Generated(
        value = "org.mapstruct.ap.MappingProcessor",
        date = "2025-07-01T21:06:20+0800",
        comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_401 (Oracle Corporation)"
    )
    @Component
    public class UserTransformImpl implements UserTransform {
    
        @Override
        public UserDto toUserDTO(User user) {
            if ( user == null ) {
                return null;
            }
    
            UserDto userDto = new UserDto();
    
            userDto.setName( user.getUsername() );
            if ( user.getAge() != null ) {
                userDto.setAge( user.getAge() );
            }
            else {
                userDto.setAge( 100 );
            }
            if ( user.getMoney() != null ) {
                userDto.setMoney( user.getMoney() );
            }
            else {
                userDto.setMoney( new java.math.BigDecimal("3000") );
            }
            userDto.setId( user.getId() );
    
            return userDto;
        }
    
        @Override
        public List toUserDtoList(List users) {
            if ( users == null ) {
                return null;
            }
    
            List list = new ArrayList( users.size() );
            for ( User user : users ) {
                list.add( toUserDTO( user ) );
            }
    
            return list;
        }
    }
    

    6)测试映射结果

    package org.coffeebeans;
    
    import org.coffeebeans.entity.User;
    import org.coffeebeans.entity.UserDto;
    import org.coffeebeans.transform.UserTransform;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Objects;
    
    /**
     * 
  • ClassName: MapstructTest
  •  *
  • Author: OakWang
  •  */
    @SpringBootTest public class MapstructTest {     @Test     public void test() {        // 创建一个User对象        List list = new ArrayList<>();        User user = User.builder().id(1).age(null).username("姓名1").address("地址1").money(null).build();        list.add(user);        // 使用UserTransform将User对象转换为UserDto对象        UserDto userDto = UserTransform.INSTANCE.toUserDTO(user);        List userDtos = UserTransform.INSTANCE.toUserDtoList(list);        // 验证转换结果        System.out.println("id属性是否相等:" + Objects.equals(user.getId(), userDto.getId()));        System.out.println("name属性是否相等:" + Objects.equals(user.getUsername(), userDto.getName()));        System.out.println("age属性是否相等:" + Objects.equals(user.getAge(), userDto.getAge()));        System.out.println("address属性是否相等:" + Objects.equals(user.getAddress(), userDto.getFamilyAddress()));        System.out.println("money属性是否相等:" + Objects.equals(user.getMoney(), userDto.getMoney()));        System.out.println("User对象:" + user);        System.out.println("UserDto对象:" + userDto);        System.out.println("User集合:" + list);        System.out.println("UserDto集合:" + userDtos);     } }

    图片

    报错处理!!

    图片

    mapstruct和lombok依赖冲突导致lombok注解全失效

    
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>3.8.1version>
                <configuration>
                    <source>1.8source>
                    <target>1.8target>
                    <annotationProcessorPaths>
                        
                        <path>
                            <groupId>org.projectlombokgroupId>
                            <artifactId>lombokartifactId>
                            <version>1.18.30version>
                        path>
                        
                        <path>
                            <groupId>org.mapstructgroupId>
                            <artifactId>mapstruct-processorartifactId>
                            <version>1.5.5.Finalversion>
                        path>
                    annotationProcessorPaths>
                configuration>
            plugin>
        plugins>
    build>
    

    总结

    以上我们了解了Mapstruct的作用、特点、常用注解、注意事项及坑点解决方案,需特别注意预编译生成target-Impl实现类,并在集成lombok使用时处理好注解冲突。

    关注公众号:咖啡Beans

    在这里,我们专注于软件技术的交流与成长,分享开发心得与笔记,涵盖编程、AI、资讯、面试等多个领域。无论是前沿科技的探索,还是实用技巧的总结,我们都致力于为大家呈现有价值的内容。期待与你共同进步,开启技术之旅。