MapStruct是一个帮助我们在处理Java Bean映射时,尽量减少样板代码的效率工具,只需要定义接口,他会自动生成映射逻辑。之前介绍的目标都是需要映射的对象,本文我们一起看看如何构建复杂的映射器。
一、准备对象
我们定义一个Article类和Person类,Person类包含两个简单字段,Article类包含两个简单字段,引用一个Person类字段,然后再定义对应的DTO类。
为了验证效果,Person类和对应的DTO类字段名不同。
以下是源类:
@Getter
@Setter
public class Article {
private int id;
private String name;
private Person author;
}
@Getter
@Setter
public class Person {
private String id;
private String name;
}
以下是目标类:
@Getter
@Setter
public class ArticleDTO {
private int id;
private String name;
private PersonDTO author;
}
@Getter
@Setter
public class PersonDTO {
private String personId;
private String personName;
}
基础类准备好了,接下来开始定义映射器。
二、将嵌套映射器定义为方法
让我们从定义一个简单的映射器开始,操作Article类:
@Mapper
public interface ArticleMapper {
ArticleMapper INSTANCE = Mappers.getMapper(ArticleMapper.class);
ArticleDTO articleToArticleDto(Article article);
}
我们看下生成的映射代码:
public class ArticleMapperImpl implements ArticleMapper {
@Override
public ArticleDTO articleToArticleDto(Article article) {
if ( article == null ) {
return null;
}
ArticleDTO articleDTO = new ArticleDTO();
articleDTO.setId( article.getId() );
articleDTO.setName( article.getName() );
articleDTO.setAuthor( personToPersonDTO( article.getAuthor() ) );
return articleDTO;
}
protected PersonDTO personToPersonDTO(Person person) {
if ( person == null ) {
return null;
}
PersonDTO personDTO = new PersonDTO();
return personDTO;
}
}
这里会自动生成Person到PersonDTO的转换。但是,因为字段名称不对应,无法自动映射,需要我们自己定义字段映射关系。
我们定义一个PersonMapper映射器:
@Mapper
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);
@Mapping(source = "id", target = "personId")
@Mapping(source = "name", target = "personName")
PersonDTO personToPersonDTO(Person person);
}
三、定义映射方法
映射器已经定义好了,如何在ArticleMapper中引用PersonMapper呢?有两种方式。
(一)手动编写映射方法
我们可以使用Mappers.getMapper()方法,在ArticleMapper中直接调用PersonMapper的映射方法:
default PersonDTO personToPersonDto(Person person) {
return Mappers.getMapper(PersonMapper.class).personToPersonDTO(person);
}
MapStruct将自动获取此方法并使用它来映射author字段,生成的代码为:
@Override
public ArticleDTO articleToArticleDto(Article article) {
if ( article == null ) {
return null;
}
ArticleDTO articleDTO = new ArticleDTO();
articleDTO.setId( article.getId() );
articleDTO.setName( article.getName() );
articleDTO.setAuthor( personToPersonDto( article.getAuthor() ) );
return articleDTO;
}
上面这种是纯手动的操作,有些粗糙,我们在看看MapStruct中如何更加优雅一些。
(二)优(zi)雅(dong)的映射
我们可以直接在@Mapper注解中使用uses参数指向我们想要使用的映射器,而不是定义一个新方法:
@Mapper(uses = PersonMapper.class)
public interface ArticleUsingPersonMapper {
ArticleUsingPersonMapper INSTANCE = Mappers.getMapper(ArticleUsingPersonMapper.class);
ArticleDTO articleToArticleDto(Article article);
}
自动生成的映射代码为:
public class ArticleUsingPersonMapperImpl implements ArticleUsingPersonMapper {
private final PersonMapper personMapper = PersonMapper.INSTANCE;
@Override
public ArticleDTO articleToArticleDto(Article article) {
if ( article == null ) {
return null;
}
ArticleDTO articleDTO = new ArticleDTO();
articleDTO.setId( article.getId() );
articleDTO.setName( article.getName() );
articleDTO.setAuthor( personMapper.personToPersonDTO( article.getAuthor() ) );
return articleDTO;
}
}
从生成的映射代码看到,虽然没有指定author字段的映射,但是MapStruct会自动从PersonMapper中找到映射方法。
是不是很方便,这才是优雅的实现。