一、简介
一个属性映射工具。
项目中各种实体之间会有很多转换,比如 vo、entity、dto、domain等。转换时需要写大量的get、set方法,冗长也工作量很大。再加上转换时可能还会有一定的业务逻辑处理,这会导致映射过程十分繁琐。
本篇博客介绍的工具MapStruct,就可以很好地解决这一问题。
二、实践
实际应用场景主要分为以下几种情况:
1、属性名相同
无需处理,自动映射,类似BeanUtils。
2、属性名不同
需手动指定。
3、属性值需额外的逻辑处理
使用表达式引入逻辑处理。
代码示例
引入jar包
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.3.0.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.3.1.Final</version>
</dependency>
代码示例
//两个实体PersonVo->PersonDto
@Data
public class PersonVo {
private Integer id;
private String personName;
private Integer type;
}
@Data
public class PersonDto {
private Integer id;
private String name;
private Integer status;
}
//映射接口
@Mapper
public interface IConvert {
IConvert INSTANCE= Mappers.getMapper(IConvert.class);
@Mappings({
@Mapping(target = "name",source = "personName"),
@Mapping(target = "status",expression = "java(ParseUtil.parseType(personVo.getType()))")
})
PersonDto convert(PersonVo personVo);
}
//expression表达式中的util方法
public class ParseUtil {
public static Integer parseType(Integer type){
switch (type){
case 1:
return 2;
case 2:
return 3;
default:
return 0;
}
}
}
//test调用
public class Test {
IConvert iConvert;
public static void main(String[] args) {
Test test=new Test();
PersonVo personVo=new PersonVo();
personVo.setId(1);
personVo.setPersonName("zhangsan");
personVo.setType(2);
PersonDto personDto = test.iConvert.INSTANCE.convert(personVo);
System.out.println(personDto);
}
}
打开编译后的文件,会发现mapstruct生成了IConvert的实现类IConvertImpl。
public class IConvertImpl implements IConvert {
public IConvertImpl() {
}
public PersonDto convert(PersonVo personVo) {
if (personVo == null) {
return null;
} else {
PersonDto personDto = new PersonDto();
personDto.setName(personVo.getPersonName());
personDto.setId(personVo.getId());
personDto.setStatus(ParseUtil.parseType(personVo.getType()));
return personDto;
}
}
}
常用注解:
@Mapper——接口注解
@Mapping——属性映射
@Mappings——可以配置多个属性
三、原理
MapStruct是在编译期间完成映射的,并没有通过反射机制动态映射。因此,安全性更高、速度更快。
MappingProcessor是MapStruct的入口,主要方法init() process()
@SupportedAnnotationTypes({"org.mapstruct.Mapper"})
@SupportedOptions({"mapstruct.suppressGeneratorTimestamp", "mapstruct.suppressGeneratorVersionInfoComment", "mapstruct.unmappedTargetPolicy", "mapstruct.defaultComponentModel"})
public class MappingProcessor extends AbstractProcessor {
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.options = this.createOptions();
this.annotationProcessorContext = new AnnotationProcessorContext(processingEnv.getElementUtils(), processingEnv.getTypeUtils());
}
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
if (!roundEnvironment.processingOver()) {
RoundContext roundContext = new RoundContext(this.annotationProcessorContext);
Set<TypeElement> deferredMappers = this.getAndResetDeferredMappers();
this.processMapperElements(deferredMappers, roundContext);
Set<TypeElement> mappers = this.getMappers(annotations, roundEnvironment);
this.processMapperElements(mappers, roundContext);
}
return false;
}
}
四、评价
优点:
BeanUtils.copyProperties()使用反射,在一定程度上会影响性能。而MapStruct是在编译时完成映射,更安全快速。另外,使用注解来映射属性,更清晰直观。
缺点:
注解中的参数值容易出错,尤其是expression,出错后编译报错。配合MapStruct插件效果会更好。