概述
现在的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!