MapStruct使用笔记
约 1129 字大约 4 分钟
2025-04-27
1. MapStruct和BeanUtils区别
MapStruct和BeanUtils都是Java中常用的对象属性映射工具,但它们在使用方式和性能上有一些区别。 Java程序执行的过程,是由编译器先把java文件编译成class字节码文件,然后由JVM去解释执行class文件。 Mapstruct正是在java文件到class这一步帮我们实现了转换方法,即做了预处理,提前编译好文件。
特性 | BeanUtils | MapStruct |
---|---|---|
使用方式 | 使用反射机制进行属性拷贝,无需额外映射代码。 | 需定义映射接口,编译阶段生成实现类,通过注解声明映射关系。 |
性能 | 反射机制导致性能较低。 | 编译时生成代码,无运行时反射,性能接近手写代码。 |
灵活性与安全性 | 动态映射,属性不匹配时可能运行时报错。 | 编译时类型检查,属性不匹配直接编译失败。支持嵌套映射、集合映射等复杂场景。 |
适用场景 | 简单、快速开发场景,对性能要求不高。 | 高性能、复杂映射场景,要求类型安全和编译期检查。 |
2. mapstruct使用
2.1. 引入依赖
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
<scope>provided</scope> <!--只影响到编译,测试阶段-->
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
<scope>provided</scope>
</dependency>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<!--父工程不需要,会多生成这个文件:standard-boot.jar.original-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<!-- repackage:这个是默认goal,在 mvn package 执行之后,这个命令再次打包生成可执行的 jar,
同时将 mvn package 生成的 jar 重命名为 *.origin-->
<goal>repackage</goal>
<!-- build-info:生成项目的构建信息文件 build-info.properties -->
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- maven 打包插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<!-- 处理器链配置 mapstruct和lombok同时使用需要配置-->
<annotationProcessorPaths>
<!--先依赖lombok,再依赖mapstruct,则属性正常转换,原理也和好理解,毕竟getter和setter是lombok编译生成的。-->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
</path>
</annotationProcessorPaths>
<!-- 在编译时保留方法参数的名称信息,以便在运行时通过反射或其他工具准确获取参数名 -->
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
</configuration>
<executions>
<!-- enable flattening -->
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<!-- ensure proper cleanup -->
<execution>
<id>flatten.clean</id>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<!-- 引入所有 匹配文件进行过滤 -->
<includes>
<include>**/*</include>
</includes>
<!-- 启用过滤 即该资源中的变量将会被过滤器中的值替换 -->
<filtering>true</filtering>
</resource>
</resources>
</build>
2.2. 映射类
package com.unravely.demo.maper;
import com.unravely.demo.dto.DateRangeDto;
import com.unravely.demo.dto.User;
import com.unravely.demo.dto.UserDto;
import org.mapstruct.*;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface UserMapper {
//https://blog.csdn.net/weixin_51360020/article/details/145170934
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@Mappings({
@Mapping(source = "user.age", target = "age", numberFormat = "#0.00", ignore = true),//忽略某个属性映射
@Mapping(source = "user.username", target = "username"),
@Mapping(source = "user.gender", target = "gender", qualifiedByName = "getGenderName"),
@Mapping(source = "user.young", target = "youngBoolean"),
@Mapping( target = "currentDay", expression = "java(java.time.LocalDateTime.now())"),
@Mapping(source = "user.birthday", target = "birth", dateFormat = "dd-MM-yyyy HH:mm:ss") //日期格式化
})
UserDto userToUserDto(User user);
/**
* 逆映射
* @param user
* @return
*/
@InheritInverseConfiguration(name = "userToUserDto")
User userDtoToUser(UserDto user);
List<UserDto> userToUserDtoList(List<User> users);
/**
* 多源映射转换
* DateRangeDto.startDate -> User.username
*/
@Mappings({
@Mapping(source = "user.age", target = "age", numberFormat = "#0.00", ignore = true),
@Mapping(source = "range.startDate", target = "username"),
@Mapping(source = "user.gender", target = "gender"), //忽略某个属性映射
@Mapping(source = "user.young", target = "youngBoolean"),
@Mapping(target = "currentDay", expression = "java(java.time.LocalDateTime.now())"),
@Mapping(source = "user.birthday", target = "birth", dateFormat = "dd-MM-yyyy HH:mm:ss") //日期格式化
})
UserDto userToUserDto(User user, DateRangeDto range);
@Named("getGenderName")
default String getGenderName(Integer gender) {
if (gender == 1) {
return "男";
} else if (gender == 0) {
return "女";
} else {
return "未知";
}
}
@Named("getGenderName")
default Integer getGenderType(String gender) {
if ("男".equals(gender)) {
return 1;
} else if ("女".equals(gender)) {
return 0;
} else {
return 2;
}
}
}
2.3. 测试
package com.unravely.demo;
import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Lists;
import com.unravely.demo.dto.DateRangeDto;
import com.unravely.demo.dto.User;
import com.unravely.demo.dto.UserDto;
import com.unravely.demo.maper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDateTime;
import java.util.List;
@SpringBootTest
public class MapstructTest {
@Test
public void coveterSingle() {
User user = new User();
user.setUsername("jack");
user.setGender(1);
user.setAge(23);
user.setYoung("true");
user.setBirthday(LocalDateTime.now());
UserDto userDto = UserMapper.INSTANCE.userToUserDto(user);
System.out.println(userDto);
}
public static void main(String[] args) {
UserDto userDto = new UserDto();
userDto.setUsername("namedto");
userDto.setAge(11);
userDto.setGender("女");
userDto.setYoungBoolean(true);
// userDto.setBirth("2024-01-01");
userDto.setCurrentDay(LocalDateTime.now());
User user = UserMapper.INSTANCE.userDtoToUser(userDto);
System.out.println(JSON.toJSONString(user));
}
private static void coveterOtherField() {
User user = new User();
user.setUsername("jack");
user.setAge(23);
user.setYoung("true");
user.setBirthday(LocalDateTime.now());
UserMapper instance = UserMapper.INSTANCE;
UserDto userDto = instance.userToUserDto(user, DateRangeDto.builder().startDate("aaaa-bb-cc").build());
System.out.println(userDto);
}
private static void coveterList() {
User user = new User();
user.setUsername("jack");
user.setAge(23);
user.setYoung("true");
user.setBirthday(LocalDateTime.now());
User user2 = new User();
user2.setUsername("tom");
user2.setAge(24);
user2.setYoung("false");
user2.setBirthday(LocalDateTime.now());
UserMapper instance = UserMapper.INSTANCE;
List<UserDto> userDtos = instance.userToUserDtoList(Lists.newArrayList(user2, user));
System.out.println(userDtos);
}
}