概述
现在的JAVA项目多数采用分层结构,参考《阿里巴巴JAVA开发手册》。
分层之后,每一层都有自己的领域模型,即不同类型的 Bean:
例如将UserDo
转换为UserVo
就很普遍,如下所示:
public class UserDo{
private String name;
private Integer age;
}
转换为:
public class UserVo {
private String name;
private Integer age;
}
手动进行对象的转换,虽然执行性能很高,但是开发效率非常低下,且可能会存在错漏的情况。因此,我们会选择借助框架或是工具来实现对象的转换,之前常用BeanUtils(有多种可选,性能对比网上找找),现在多了一个选择就是MapStruct。
MapStruct
简介
MapStruct 是一个JSR 269 的 Java 注解处理器,是它是基于注解的,而且是编译时APT(annotation processor tool)。不像其他APT是运行时,例如Spring里面的注解处理方式,是在运行时通过反射的方式处理的。
使用
maven配置
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<mapstruct.version>1.5.3.Final</mapstruct.version>
<lombok.version>1.18.20</lombok.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok-mapstruct-binding.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
应用
假设我们有如下两个需要转换的类:
@Data
public class UserDo {
private String name;
private String gender;
private Double height;
private Date birthday;
private Address address;
private String girlName;
private String girlDes;
}
@Data
public class UserVo {
private String name;
private String gender;
private String height;
private String bornDay;
private AddressDto address;
private GirlFriendDto girlFriend;
}
第一步: 定义一个interface,使用 @Mapper
标记。
第二步:构建一个实例属性用于访问里面的方法。
@Mapper
public interface UserConvetor {
UserConvetor INSTANCE = Mappers.getMapper(UserConvetor.class);
}
第三步:提供转换方法申明,必要时使用@Mapping
注解
@Mapper
public interface UserConvetor {
UserConvetor INSTANCE = Mappers.getMapper(UserConvetor.class);
@Mapping(target = "bornDay", source = "birthday")
UserVo toUserVo(UserDo userDo);
}
自定义映射
MapStruc默认会将两个bean的名称相同的属性进行映射,如果source与target的属性名称不一致则需要借助@Mapping
注解。编译程序后就会在\target\generated-sources\annotations
下产生UserConvetorImpl实现类了。
第四步:代码中使用UserConvetor转换类
public void runConvetor(){
UserDo userDo = new UserDo();
userDo.setName("张三");
...
UserVo userVo= UserConvetor.INSTANCE.toUserVo(userDo);
log.info("vo: {}", userVo);
}
忽略映射
如果不想给UserDo
的birthday
赋值可以忽略它。
@Mapping(target = "bornDay", ignore = true)
UserVo toUserVo(UserDo userDo);
设置默认值
如果想实现在source值为null时给一个默认值也是可以了。
@Mapping(target = "gender", defaultValue = "man")
UserVo toUserVo(UserDo userDo);
设置常量
@Mapping(target = "height", constant = "175")
数据类型转换
日期类型转换
@Mapping(target = "bornDay", source = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
UserVo toUserVo(UserDo userDo);
小数点位数转换
@Mapping(target = "height", source = "height" ,numberFormat = "#.00")
表达式
大写转换
@Mapping(target = "name", expression = "java(programer.getName().toUpperCase())")
方法调用
@Mapping(target = "name", expression = "java(nameToUp(userDo))")
UserVo toUserVo(UserDo userDo);
default String nameToUp(UserDo userDo){
return Optional.ofNullable(userDo)
.filter(Objects::nonNull)
.map(u->u.getName())
.orElse(null)
.toUpperCase();
}
嵌套映射
我们经常会遇到bean里面套着bean的映射。
{
"name":"shusheng007",
"address":{
"country":"China",
"city":"TianJin"
}
}
对于这样的映射,我们只需要在mapper中提供一个嵌套bean的转换关系即可。
@Mapping(target = "address", source = "address")
UserVo toUserVo(UserDo userDo);
//嵌套bean的转换关系
AddressDto toAddressDto(Address addr);
集合映射
只需要提供集合元素类型的映射即可。
AddressDto toAddressDto(Address addr);
List<AddressDto> toAddressList(List<Address> addrList);
多个数据对象
@Mapping(target = "name", source = "programer.name")
@Mapping(target = "girlFriendName", source = "girl.name")
UserVo toUserVo(UserDo userDo, Gir girl);
其他还有外部引用、切面操作在此就不一一列举。
IDE 插件
MapStruct 提供了 IDEA MapStruct Support 插件,让我们在 IDEA 中,可以更愉快的使用 MapStruct!