mapstruct是什么?
官网地址:https://mapstruct.org/
官方定义:MapStruct 是一个代码生成器,它基于约定优于配置的方法,极大地简化了 Java bean 类型之间的映射实现。生成的映射代码使用普通的方法调用,因此速度快、类型安全且易于理解。
个人:MapStruct 类似于org.springframework.beans.BeanUtils#copyProperties(java.lang.Object, java.lang.Object),可以实现对象的复制,不同的是它不基于反射实现,不需要考虑内存等问题,而是类似于代码生成器,生成对应的set方法,调用对象的get方法获取值。
MapStruct是用来做什么的?
现在有这么个场景,从数据库查询出来了一个user对象(包含id,用户名,密码,手机号,邮箱,角色这些字段)和一个对应的角色对象role(包含id,角色名,角色描述这些字段),现在在controller需要用到user对象的id,用户名,和角色对象的角色名三个属性。一种方式是直接把两个对象传递到controller层,但是这样会多出很多没用的属性。更通用的方式是需要用到的属性封装成一个类(DTO),通过传输这个类的实例来完成数据传输。
怎么用?
pom.xml加入如下配置:
<dependency>
<groupId>org.mapstruct</groupId>
<!-- jdk8以下就使用mapstruct -->
<artifactId>mapstruct-jdk8</artifactId>
<version>1.2.0.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.2.0.Final</version>
</dependency>
demo
先定义一个User对象:
@Data
@Builder
public class User {
private String name;
private String createTime;
private Integer status;
}
再定义几个用于测试转换的对象:
// 字段类型与名称完全一致
@Data
public class UserDTO {
private String name;
private String createTime;
private Integer status;
}
// 字段名称有不一样的
@Data
public class UserNameDTO {
private String userName;
private String createTime;
private Integer status;
}
// 字段类型有不一样的
@Data
public class UserTypeDTO {
private String name;
@DateTimeFormat
private LocalDateTime createTime;
private String status;
}
转换的接口类
@Mapper(componentModel = "spring")
@Component
public interface UserConvert {
/**
* 字段名、类型完全一致
* @param user
* @return
*/
UserDTO toConvertUserDTO(User user);
/**
* 字段类型不一样,包含枚举转换
* @param user
* @return
*/
@Mappings({
@Mapping(target = "createTime",expression = "java(com.dbl.demo.utils.DateUtil.strToDate(user.getCreateTime()))"),
@Mapping(target = "status",expression = "java(com.dbl.demo.constants.StatusEnum.toEnumByValue(user.getStatus()).getName())")
})
UserTypeDTO toConvertUserTypeDTO(User user);
/**
* 字段名称不一样
* @param user
* @return
*/
@Mapping(source = "name",target = "userName")
UserNameDTO toConvertUserNameDTO(User user);
}
对于需要特殊处理的字段,可以用expression调用自定义的方法对字段进行转换,比如status字段需要取出枚举的描述值,就用expression = "java(com.dbl.demo.constants.StatusEnum.toEnumByValue(user.getStatus()).getName())"
用到的枚举类型
public enum StatusEnum {
DRAFT(1,"草稿"),
DONE(2,"完成");
StatusEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
private Integer value;
private String name;
public Integer getValue() {
return value;
}
public String getName() {
return name;
}
public static StatusEnum toEnumByValue(Integer value) {
StatusEnum s = null;
for (StatusEnum e : StatusEnum.values()) {
if (e.getValue().equals(value)) {
s = e;
break;
}
}
return s;
}
}
用到的日期转换方法
public class DateUtil {
public static LocalDateTime strToDate(String str){
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDateTime.parse(str,df);
}
}
测试的接口
@Slf4j
@RestController
@RequestMapping("/mapstruct")
public class MapStructTest {
@Autowired
private UserConvert userConvert;
@GetMapping
public String test() {
User user = User.builder().name("dbl").createTime("2022-03-01 10:00:00").status(1).build();
log.info("[base user]: {}", JSON.toJSONString(user));
UserDTO userDTO = userConvert.toConvertUserDTO(user);
log.info("[类型名称完全一致]: {}", JSON.toJSONString(userDTO));
UserTypeDTO userTypeDTO = userConvert.toConvertUserTypeDTO(user);
log.info("[类型不一致]: {}", JSON.toJSONString(userTypeDTO));
UserNameDTO userNameDTO = userConvert.toConvertUserNameDTO(user);
log.info("[字段名不一致]: {}", JSON.toJSONString(userNameDTO));
return "success";
}
}
原理
官方定义里面页说了MapStruct 是一个代码生成器,编译项目后其实我们可以看到生成了一个原来接口的实现类。
原本代码结构如下:
编译后可以看到多了一个UserConvertImpl的实现类:
实现类的代码如下,可以看到实际是调用的对象的get,set方法对字段进行赋值:
@Component
public class UserConvertImpl implements UserConvert {
public UserConvertImpl() {
}
public UserDTO toConvertUserDTO(User user) {
if (user == null) {
return null;
} else {
UserDTO userDTO = new UserDTO();
userDTO.setName(user.getName());
userDTO.setCreateTime(user.getCreateTime());
userDTO.setStatus(user.getStatus());
return userDTO;
}
}
public UserTypeDTO toConvertUserTypeDTO(User user) {
if (user == null) {
return null;
} else {
UserTypeDTO userTypeDTO = new UserTypeDTO();
userTypeDTO.setName(user.getName());
userTypeDTO.setCreateTime(DateUtil.strToDate(user.getCreateTime()));
userTypeDTO.setStatus(StatusEnum.toEnumByValue(user.getStatus()).getName());
return userTypeDTO;
}
}
public UserNameDTO toConvertUserNameDTO(User user) {
if (user == null) {
return null;
} else {
UserNameDTO userNameDTO = new UserNameDTO();
userNameDTO.setUserName(user.getName());
userNameDTO.setCreateTime(user.getCreateTime());
userNameDTO.setStatus(user.getStatus());
return userNameDTO;
}
}
}