介绍
编译期即可生成对象复制代码。简单理解,功能定位org.springframework.beans.BeanUtils
。
官网,GitHub-MapStruct。
入门
maven项目引入依赖:
- mapstruct:包含必要注解,如@Mapping
- mapstruct-processor:注解处理器,根据注解自动生成mapper实现
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
</dependency>
实战:
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
// 如果增加componentModel 增生成的实现类有注解@Component
@Mapper(componentModel = "spring")
public interface AccountConvert {
AccountConvert INSTANCE = Mappers.getMapper(AccountConvert.class);
@Mappings({
@Mapping(target = "username", source = "userName")
@Mapping(source = "userTypeEnum", target = "type")
@Mapping(target = "createTime", expression = "java(com.java.mmzsblog.util.DateTransform.strToDate(source.getCreateTime()))"),
})
Account dto2Entity(AccountDTO dto);
@Data
private static class Account {
private string username;
}
@Data
private static class AccountDTO {
private string userName;
}
@Getter
@AllArgsConstructor
public enum UserTypeEnum {
JAVA("000", "Java开发工程师"),
DB("001", "数据库管理员");
private String value;
private String title;
}
生成class文件:
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2023-06-10T12:52:02+0800",
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 11.0.18 (Azul Systems, Inc.)"
)
public class AccountConvertImpl implements AccountConvert {
@Override
public Account dto2Entity(AccountDTO dto) {
if (dto == null) {
return null;
}
Account account = new Account();
account.setUsername(dto.getUserName());
return account;
}
}
Spring Bean
使用@Mapper注解时,如果备注componentModel = "spring"
,则可以把AccountConvert
当做一个Spring Bean来使用:
@Resource
private DrugConverter drugConverter;
原理
当字段类型不一致时,MapStruct能够自动实现类型转换的类型:
- 基本类型及其对应的包装类型
- 基本类型的包装类型和String类型之间
除此之外的类型转换,可以通过定义表达式来进行指定转换。
注解
- @Mapper,用于接口类,可以理解为定义一个静态类或Spring Bean类
- @Mapping,指定source和target实体类的映射关系
- @Mappings,多个@Mapping的合集
- @MapMapping:
- @BeanMapping:
- @BeforeMapping & @AfterMapping:类似于Junit的@BeforeTest和@AfterTest,在@Mapping之前和之后做一些处理
- @InheritConfiguration:
- @InheritInverseConfiguration:已经存在DTO转PO的接口方法定义时,需要一个PO转DTO的接口时,可使用此注解,省去source、target、dateFormat等信息的配置维护
数据类型映射
MapStruct支持source和target属性之间的数据类型转换,还提供类型自动转换,适用于:
- 基本类型及其对应的包装类之间
- 任意基本类型与任意包装类之间,如 byte 和 Integer
- 所有基本类型及包装类与String之间
- 枚举和String之间
- Java大数类型(
java.math.BigInteger
,java.math.BigDecimal
) 和Java基本类型(包括其包装类)与String之间。
日期转换,可使用dateFormat标志指定日期格式;
数字转换,可使用numberFormat指定显示格式
枚举映射
实战
记录使用遇到的问题
Internal error in the mapping processor: java.lang.NullPointerException
IDEA 2022.1.4 (Ultimate Edition)版本,以Debug模式启动应用,失败:
java: Internal error in the mapping processor: java.lang.NullPointerException
at org.mapstruct.ap.internal.processor.DefaultVersionInformation.createManifestUrl(DefaultVersionInformation.java:182) at org.mapstruct.ap.internal.processor.DefaultVersionInformation.openManifest(DefaultVersionInformation.java:153) at org.mapstruct.ap.internal.processor.DefaultVersionInformation.getLibraryName(DefaultVersionInformation.java:129) at org.mapstruct.ap.internal.processor.DefaultVersionInformation.getCompiler(DefaultVersionInformation.java:122)
at org.mapstruct.ap.internal.processor.DefaultVersionInformation.fromProcessingEnvironment(DefaultVersionInformation.java:95)
at org.mapstruct.ap.internal.processor.DefaultModelElementProcessorContext.<init>(DefaultModelElementProcessorContext.java:54)
at org.mapstruct.ap.MappingProcessor.processMapperElements(MappingProcessor.java:264)
at org.mapstruct.ap.MappingProcessor.process(MappingProcessor.java:166)
at org.jetbrains.jps.javac.APIWrappers$ProcessorWrapper.process(APIWrappers.java:157)
参考,解决方法:升级MapStruct pom依赖,1.4.0.Final
-> 当前最新版1.5.5.Final
。
No target bean properties found: can’t map Collection element, Consider to declare/implement a mapping method
应用以Debug模式启动报错:
java: No target bean properties found: can't map Collection element "DrugEsEntity drugEsEntity" to "DrugListVo drugListVo". Consider to declare/implement a mapping method: "DrugListVo map(DrugEsEntity value)".
现有接口定义:
@Mapper
public interface DrugConverter {
DrugConverter INSTANCE = Mappers.getMapper(DrugConverter.class);
List<DrugListVo> esToVos(List<DrugEsEntity> entities);
}
解决方法,增加接口:
DrugListVo esToVo(DrugEsEntity entity);
这在生成的DrugConverterImpl
代码中可见一斑:
参考