摘要
本文介绍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、资讯、面试等多个领域。无论是前沿科技的探索,还是实用技巧的总结,我们都致力于为大家呈现有价值的内容。期待与你共同进步,开启技术之旅。