Java实现MapStruct对象转换的示例代码
作者:scj1022
一、基本使用
1、Maven 引入
<properties>
<lombok.version>1.18.30</lombok.version>
<mapstruct.version>1.4.1.Final</mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</dependency>
</dependencies>
2、基本使用
待转换的类
@Data
@Builder
public class UserDTO {
private Long id;
private Integer age;
private String name;
}
转换目标类
@Data
public class UserVO {
private Long id;
private Integer age;
private String name;
}
转换器
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface Converter {
// 后续通过 INSTANT 调用转换方法
Converter INSTANT = Mappers.getMapper(Converter.class);
UserVO convert(UserDTO userDTO);
}
使用示例
public class Test {
public static void main(String[] args) {
UserDTO userDTO = UserDTO.builder().id(1L).age(18).name("scj").build();
UserVO userVO = Converter.INSTANT.convert(userDTO);
System.out.println(userVO);
}
}
输出结果
UserVO(id=1, age=18, name=scj)
可以看到在 target 生成 转换器的实现类,并使用 get 和 set 进行转换,所以性能很高。

3、Spring Bean
MapStruct提供了依赖注入的机制,让我们能够在Spring的环境下,更优雅的获得Converter。
转换器
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = MappingConstants.ComponentModel.SPRING)
public interface Converter {
UserVO convert(UserDTO userDTO);
}
使用示例
@RestController
public class TestController {
// 直接注入使用
@Autowired
private Converter converter;
@GetMapping("/test")
public void mapstructTest() {
UserDTO userDTO = UserDTO.builder().id(1L).age(18).name("scj").build();
UserVO userVO = converter.convert(userDTO);
System.out.println(userVO);
}
}
输出结果
UserVO(id=1, age=18, name=scj)
可以看到 target 中生成的实现类交给 Spring容器 管理了

二、类型一致的场景
1、字段名不一致
字段名不同,通过 target 和 source 属性指定映射关系(字段名一致可以省略)
转换器
@Mapper
public interface Converter {
Converter INSTANT = Mappers.getMapper(Converter.class);
@Mapping(target = "userName", source = "name")
UserVO convert(UserDTO userDTO);
}
输出结果
UserVO(id=1, age=18, username=scj)
2、ignore
不需要映射的字段,可以设置 ignore = true
转换器
@Mapper
public interface Converter {
Converter INSTANT = Mappers.getMapper(Converter.class);
// 不需要映射的字段,指定 ignore = true
@Mapping(target = "name", ignore = true)
@Mapping(target = "age", ignore = true)
UserVO convert(UserDTO userDTO);
}
输出结果
UserVO(id=1, age=null, name=null)
3、defaultValue
通过 defaultValue 设置默认值(仅当源对象的该属性值为 null 时)
转换器
@Mapper
public interface Converter {
Converter INSTANT = Mappers.getMapper(Converter.class);
// 只有为 null 的才会生效
@Mapping(target = "id", defaultValue = "1")
@Mapping(target = "age", defaultValue = "66")
@Mapping(target = "name", defaultValue = "zs")
UserVO convert(UserDTO userDTO);
}
使用示例
public class Test {
public static void main(String[] args) {
// id = null
UserDTO userDTO = UserDTO.builder().age(18).name("scj").build();
UserVO userVO = Converter.INSTANT.convert(userDTO);
System.out.println(userVO);
}
}
输出结果
UserVO(id=1, age=18, name=scj)
4、constant
通过 constant 为属性设置常量值,无论源对象的该属性值是否为 null
转换器
@Mapper
public interface Converter {
Converter INSTANT = Mappers.getMapper(Converter.class);
// 只有为 null 的才会生效
@Mapping(target = "id", constant = "6")
@Mapping(target = "age", constant = "66")
@Mapping(target = "name", constant = "zs")
UserVO convert(UserDTO userDTO);
}
使用示例
public class Test {
public static void main(String[] args) {
UserDTO userDTO = UserDTO.builder().id(1).age(18).name("scj").build();
UserVO userVO = Converter.INSTANT.convert(userDTO);
System.out.println(userVO);
}
}
输出结果
UserVO(id=6, age=66, name=zs)
三、类型不一致的场景
1、默认转换
对于部分数据类型,如果属性类型不一致,会做默认的的转换并赋值。
包装类 相关:基本数据类型、字符串、其他包装类
// Integer -> int target.setIntegerValue( source.getIntegerValue() ); // Integer -> Long target.setIntegerValue( source.getIntegerValue().longValue() ); // Integer -> Double target.setIntegerValue( source.getIntegerValue().doubleValue() ); // Integer -> String target.setIntegerValue( String.valueOf( source.getIntegerValue() ) );
BigDecimal 相关:
// BigDecimal -> Integer/int target.setDecimalValue( source.getDecimalValue().intValue() ); // BigDecimal -> Double/double target.setDecimalValue( source.getDecimalValue().doubleValue() ); // BigDecimal -> String target.setDecimalValue( source.getDecimalValue().toString() );
时间日期 相关
// Date -> LocalDateTime target.setDateValue( LocalDateTime.ofInstant( source.getDateValue().toInstant(), ZoneId.of( "UTC" ) ) ); // LocalDateTime -> Date target.setDateValue( Date.from( source.getDateValue().toInstant( ZoneOffset.UTC ) ) ); // Date -> String target.setDateValue( new SimpleDateFormat().format( source.getDateValue() ) ); // LocalDateTime -> String target.setDateValue( DateTimeFormatter.ISO_LOCAL_DATE_TIME.format( source.getDateValue() ) );
枚举 相关
// Enum -> String target.setEnumValue( source.getEnumValue().name() );
String 相关
// String -> Long/long target.setStringValue( Long.parseLong( source.getStringValue() ) ); // String -> BigDecimal target.setStringValue( new BigDecimal( source.getStringValue() ) ); // String -> Date target.setStringValue( new SimpleDateFormat().parse( source.getStringValue() ) ); // String -> LocalDateTime target.setStringValue( LocalDateTime.parse( source.getStringValue() ) ); // String -> Enum target.setEnumValue( Enum.valueOf( Source.CustomEnum.class, source.getEnumValue() ) );
2、numberFormat
数字类型 转换为 字符串 时,可以通过 numberFormat 属性指定 格式
@Mapper
public interface Converter {
Converter INSTANT = Mappers.getMapper(Converter.class);
@Mapping(target = "integerValue", source = "integerValue", numberFormat = "#,##0")
@Mapping(target = "doubleValue", source = "doubleValue", numberFormat = "#0.00")
@Mapping(target = "decimalValue", source = "decimalValue", numberFormat = "#%")
Target convert(Source source);
}
#,##0:表示数字使用千位分隔符。例如,1234567将被格式化为1,234,567。#0.00:表示数字保留两位小数。例如,666.666将被格式化为666.67。#%:表示将数字视为百分比。例如,0.75将被格式化为75%。
3、dateFormat
时间日期 转换为 字符串 时,可以通过 dateFormat 属性指定 pattern
待转换的类
@Data
public class Source {
private Date dateValue = new Date();
private LocalDateTime localDateTimeValue = LocalDateTime.now();
}
转换目标类
@Data
public class Target {
private String dateValue;
private String localDateTimeValue;
}
转换器
@Mapper
public interface Converter {
Converter INSTANT = Mappers.getMapper(Converter.class);
@Mapping(target = "dateValue", source = "dateValue", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Mapping(target = "localDateTimeValue", source = "localDateTimeValue", dateFormat = "yyyy-MM-dd HH:mm:ss")
Target convert(Source source);
}
输出结果
Target(dateValue=2024-09-26 19:27:41, localDateTimeValue=2024-09-26 19:27:41)
4、枚举值处理
待转换的类
@Data
public class Source {
private SexEnum sexEnum = SexEnum.MAN;
}
转换目标类
@Data
public class Target {
private String sex;
}
转换器
@Mapper
public interface Converter {
Converter INSTANT = Mappers.getMapper(Converter.class);
@Mapping(target = "sex", source = "sexEnum.desc")
Target convert(Source source);
}
四、其他场景
1、对象嵌套(字段一致)
如果字段完全一致,会自动生成嵌套对象的 convert 方法。
待转换的类
@Data
public class Source {
private Long id = 1L;
private SourceInnerClass1 inner = new SourceInnerClass1();
}
@Data
class SourceInnerClass1 {
private Long id = 11L;
private List<SourceInnerClass2> innerList = Collections.singletonList(new SourceInnerClass2());
}
@Data
class SourceInnerClass2 {
private Long id = 111L;
private String value = "inner2";
}
转换目标类
@Data
public class Target {
private Long id;
private TargetInnerClass1 inner;
}
@Data
class TargetInnerClass1 {
private Long id;
private List<TargetInnerClass2> innerList;
}
@Data
class TargetInnerClass2 {
private Long id;
private String value;
}
转换器
@Mapper
public interface Converter {
Converter INSTANT = Mappers.getMapper(Converter.class);
Target convert(Source source);
}
输出
Target(id=1, inner=TargetInnerClass1(id=11, innerList=[TargetInnerClass2(id=111, value=inner2)]))
可以看到,所有嵌套对象的属性都做了自动映射


2、对象嵌套(字段不一致)
如果字段不一致,需要自己编写对应嵌套对象的 convert 方法,底层会自动调用。
待转换的类
@Data
public class Source {
private Long id;
private SourceInnerClass1 inner;
}
@Data
class SourceInnerClass1 {
private Long id;
private List<SourceInnerClass2> innerList;
}
@Data
class SourceInnerClass2 {
private Long id;
private String value;
}
转换目标类
@Data
public class Target {
private Long id0;
private TargetInnerClass1 inner0;
}
@Data
class TargetInnerClass1 {
private Long id1;
private List<TargetInnerClass2> innerList1;
}
@Data
class TargetInnerClass2 {
private Long id2;
private String value2;
}
转换器
@Mapper
public interface Converter {
Converter INSTANT = Mappers.getMapper(Converter.class);
@Mapping(target = "id0", source = "id")
@Mapping(target = "inner0", source = "inner")
Target convert(Source source);
@Mapping(target = "id1", source = "id")
@Mapping(target = "innerList1", source = "innerList")
TargetInnerClass1 convert(SourceInnerClass1 source);
@Mapping(target = "id2", source = "id")
@Mapping(target = "value2", source = "value")
TargetInnerClass2 convert(SourceInnerClass2 source);
}
使用示例
public class Test {
public static void main(String[] args) {
final Source source = new Source(1L, 18L, "scj", new SourceInner(1L, 18));
final Target target = Converter.INSTANT.convert(source);
System.out.println(target);
}
}
输出
Target(id0=1, inner0=TargetInnerClass1(id1=11, innerList1=[TargetInnerClass2(id2=111, value2=inner2)]))
会按需调用对应对象的 convert 方法

3、自定义转换方法
使用 @Named 自定义转换方法,通过 qualifiedByName 属性指定,要和 @Named 的 value 对应
待转换的类
@Data
public class Target {
private UserDTO targetUserDTO;
private String targetUserJson;
}
转换目标类
@Data
public class Source {
private String sourceUserJson;
private UserDTO sourceUserDTO;
}
转换器
@Mapper
public interface Convert {
Convert INSTANCE = Mappers.getMapper(Convert.class);
@Mapping(target = "targetUserDTO", source = "sourceUserJson", qualifiedByName = "json2DTO")
@Mapping(target = "targetUserJson", source = "sourceUserDTO", qualifiedByName = "dto2Json")
Target convert(Source source);
@Named("json2DTO")
default UserDTO json2DTO(String userJson) {
return JSON.parseObject(userJson, UserDTO.class);
}
@Named("dto2Json")
default String dto2Json(UserDTO userDTO) {
return JSON.toJSONString(userDTO);
}
}
转换结果
Target(targetUserDTO=UserDTO(name=scj, age=18), targetUserJson={"age":66,"name":"zs"})
4、expression
通过
expression可以指定java表达式,可以直接调用对应的方法。
转换器
@Mapper
public interface Convert {
Convert INSTANCE = Mappers.getMapper(Convert.class);
// 本类方法
@Mapping(target = "targetUserDTO", expression = "java(json2DTO(source.getSourceUserJson()))")
// 非本类方法(类全名调用即可)
@Mapping(target = "targetUserJson", expression = "java(com.alibaba.fastjson.JSON.toJSONString(source.getSourceUserDTO()))")
// 枚举
@Mapping(target = "sex", expression = "java(source.getSexEnum().getDesc())")
Target convert(Source source);
default UserDTO json2DTO(String userJson) {
return JSON.parseObject(userJson, UserDTO.class);
}
}
转换结果
Target(targetUserDTO=UserDTO(name=scj, age=18), targetUserJson={"age":66,"name":"zs"}, sex=男)
ConvertImpl
public class ConvertImpl implements Convert {
@Override
public Target convert(Source source) {
if ( source == null ) {
return null;
}
Target target = new Target();
target.setTargetUserDTO( json2DTO(source.getSourceUserJson()) );
target.setTargetUserJson( com.alibaba.fastjson.JSON.toJSONString(source.getSourceUserDTO()) );
target.setSex( source.getSexEnum().getDesc() );
return target;
}
}
5、多个参数
待转换的类
@Data
public class Target {
// source1
private String strValue1;
private Integer intValue1;
// source2
private String strValue2;
private Integer intValue2;
// field
private String strValue;
private Integer intValue;
}
转换目标类
@Data
public class Source1 {
private String strValue = "s1";
private Integer intValue = 1;
}
@Data
public class Source2 {
private String strValue = "s2";
private Integer intValue = 2;
}
转换器
@Mapper
public interface Convert {
Convert INSTANCE = Mappers.getMapper(Convert.class);
@Mapping(target = "strValue1", source = "s1.strValue")
@Mapping(target = "intValue1", source = "s1.intValue")
@Mapping(target = "strValue2", source = "s2.strValue")
@Mapping(target = "intValue2", source = "s2.intValue")
@Mapping(target = "strValue", source = "strValue")
@Mapping(target = "intValue", source = "intValue")
Target convert(Source1 s1, Source2 s2, String strValue, Integer intValue);
}
注意事项:如果字段名全部没有冲突,不需要 @Mapping,会自动对应。
五、推荐的IDEA插件
IDEA 搜索 MapStruct Support 安装即可,可以在使用MapStruct时获得更加丰富代码提示。

到此这篇关于Java实现MapStruct对象转换的示例代码的文章就介绍到这了,更多相关Java MapStruct对象转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
