在开始之前,我们来说一说传统的代码开发风格:
首先,我们要有dao、service、controller三层结构。dao用来操作DB,service层封装具体的业务逻辑,Controller层面向前端页面。复杂点的项目会引入一个bo层,也就是dao和service的中间层。比如单表的一些相对复杂的操作封装在bo里,service层只处理实体与实体之间的关系。
当我们需要在前端页面展示多个实体数据的时候,我们是这样做的:
1.新建一个vo对象
2.vo对象包含我们所需要返回的实体属性
3.然后通过各类properties copy工具来复制属性
过程非常的无聊、甚至痛苦,但是又毫无办法。
得益于有了mapStruct这个组件,让pojo转vo不在痛苦。
一、mapStruct环境准备
导入依赖,一般是结合lombok一起使用,不然略显单调(lombok的使用说明可以查看之前文章):
<dependency>
<groupId>org.mapstruct</groupId>
<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>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
其实还有另外一种方式,就是基于maven插件的做法,就可以不用导入mapstruct-processor
了。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${java.encoding}</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
但是一般还是使用的第一种方式。
然后在idea->settings->annotation processors->启用注解处理
二、使用
准备一个Goods 对象和User对象:
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class Goods {
private Integer id;
private String descs;
private double prices;
private String name;
private String about;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class User {
private int userId;
private String userName;
private int age;
private String description;
}
现在我需要拿到商品和用户信息,只需要新建一个DTO--GoodsInfoDTO:
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@ToString
public class GoodsInfoDto {
private Integer goodsId;
private String descs;
private double goodsPrices;
private String goodsName;
private String about;
private int userId;
private String userName;
private int age;
private String userDescs;
}
然后一个一个的set属性吗?NO,来看mapStruct怎么做的。
新建一个converter类,名字随意,我这里叫GoodsInfoConverter
:
@Mapper
public interface GoodsInfoConverter {
GoodsInfoConverter INSTANCE = Mappers.getMapper(GoodsInfoConverter.class);
@Mappings({@Mapping(source = "goods.id", target = "goodsId"),
@Mapping(source = "goods.prices", target = "goodsPrices"),
@Mapping(source = "goods.name",target = "goodsName"),
@Mapping(source = "user.description",target = "userDescs"),
}
)
public GoodsInfoDto goods2GoodsInfoDto(Goods goods, User user);
}
其中的GoodsInfoConverter INSTANCE = Mappers.getMapper(GoodsInfoConverter.class);
必不可少,类似于一个SPI接口,就可以让你再客户端通过GoodsInfoConverter.INSTANCE获取到它的实例。
下面的@Mappings就是对象-DTO之间的映射关系,这里要转换goods
、user
对象到DTO,所以传入了两个参数。其中的source
是需要转换的对象属性,如果有多个对象需要转换,则需要在前面加入对应的对象名,例如goods.id
,用过hibernate的同学会觉得格外小清新~.其中的target
是DTO中的属性名,如果需要转换的对象和DTO属性名相同,则不需要配置。还有一些其它参数,我就不一一介绍了。
最重要的一步,就是编译,mvn clean一下:
你会发现在target目录的generated-sources下会生成一个GoodsInfoConverterImpl
:
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2018-12-20T15:44:31+0800",
comments = "version: 1.2.0.Final, compiler: javac, environment: Java 1.8.0_181 (Oracle Corporation)"
)
public class GoodsInfoConverterImpl implements GoodsInfoConverter {
@Override
public GoodsInfoDto goods2GoodsInfoDto(Goods goods, User user) {
if ( goods == null && user == null ) {
return null;
}
GoodsInfoDto goodsInfoDto = new GoodsInfoDto();
if ( goods != null ) {
goodsInfoDto.setGoodsPrices( goods.getPrices() );
goodsInfoDto.setGoodsName( goods.getName() );
goodsInfoDto.setGoodsId( goods.getId() );
goodsInfoDto.setDescs( goods.getDescs() );
goodsInfoDto.setAbout( goods.getAbout() );
}
if ( user != null ) {
goodsInfoDto.setUserDescs( user.getDescription() );
goodsInfoDto.setUserId( user.getUserId() );
goodsInfoDto.setUserName( user.getUserName() );
goodsInfoDto.setAge( user.getAge() );
}
return goodsInfoDto;
}
}
完全是自动生成的,你不需要管。当我们mvn打包的时候会自动将该文件打包进去。
客户端怎么使用呢?来看一下:
Goods goods = new Goods();
goods.setId(3);
goods.setDescs("飞机");
goods.setPrices(32000.0D);
goods.setName("无人机");
User user = new User();
user.setUserId(13581);
user.setUserName("jerrik");
user.setAge(25);
user.setDescription("帅小伙");
GoodsInfoDto goodsInfoDto = GoodsInfoConverter.INSTANCE.goods2GoodsInfoDto(goods,user);
System.out.println(goodsInfoDto);
//GoodsInfoDto(goodsId=3, descs=飞机, goodsPrices=32000.0, goodsName=无人机, about=null, userId=13581, userName=jerrik, age=25, userDescs=帅小伙)
是不是很简单,而且省去了很多事,比bean copy的性能也要好。完全是基于set来实现的。
三、注意事项
mapStruct集成lombok时,lombok的版本设置成新版本吧。反正笔者1.16.8的lombok会报错,提示Mappings找不到映射的属性,1.16.18是可以编译通过的。如果你觉得这些还不够用,可以去查看官网,谢谢~
网友评论