日常开发中,我们常常需要将业务相关的各种PO,VO,DTO对象互相转换,比较常用的工具类就是 BeanUtil ,但是BeanUtil有两个问题:
- 底层使用了反射,效率不高
- 无法自定义转换规则,例如其中一个bean中的datetime是Date类型,而另一个bean中的datetime是String类型,这就无法转换过去了
这里我要推荐一个比较好用的bean转换工具:MapStruct
MapStruct用法
准备工作
创建maven项目,引入以下依赖
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.3.0.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.3.0.Final</version>
</dependency>
准备两个实体类 DogPO
和 DogVO
,为了偷懒,我用了lombok插件。
/**
* VO类
*/
@Data
public class DogVO {
private int id;
private String name;
private int weight;
private Date birthday;
}
/**
* PO类
*/
@Data
public class DogPO {
private int id;
private String name;
private int weight;
private Date birthday;
}
最普通的转换
完成以上准备之后,我们编写一个 DogConvertor
接口
@Mapper //注意这里的注解是org.mapstruct.Mapper,不要引入错了,mybatis包也有个的@Mapper注解
public interface DogConvertor {
DogConvertor INSTANCE = Mappers.getMapper(DogConvertor.class);
/**
* 将VO转为PO
* @param vo
* @return
*/
DogPO voToPO(DogVO vo);
}
测试代码:
public static void main(String[] args) {
//创建一个VO类
DogVO dogVO = new DogVO();
dogVO.setId(1);
dogVO.setName("旺财");
dogVO.setWeight(20);
dogVO.setBirthday(new Date());
//将VO类转换为PO类,实际上就是创建一个PO类,并将VO类的属性值都拷贝过去
DogPO dogPO = DogConvertor.INSTANCE.voToPO(dogVO);
System.out.println(dogPO);
}
输出结果
我们看到PO类的属性全都被赋值好了。
为啥我们只写了一个接口,没有写任何的转换逻辑,就可以自动将 DogVO 转换为 DogPO 呢?
找到编译后的字节码目录下,会看到一个Impl文件。
我们将这个Impl打开(使用Idea打开class文件会自动为我们反编译的)
这就很好理解了MapStruct在编译阶段会识别我们的接口,生成一个实现类,在实现类中,为我们实现了set逻辑例如,上面的例子中,给DogCovertor接口,生成了一个实现类DogCovertorImpl
属性名不同的转换
我们将 DogPO 中的 name
属性改为 dogName
,这样一来我们该如何将 DogVO 中的 name
属性转换到 dogName
上呢?MapStruct很贴心的为我们提供了 @Mapping
注解
@Mapper
public interface DogConvertor {
DogConvertor INSTANCE = Mappers.getMapper(DogConvertor.class);
/**
* 将VO转为PO
* @param vo
* @return
*/
@Mapping(source = "name", target = "dogName")
DogPO voToPO(DogVO vo);
}
##集合转换
如果我们想要将List<DogVO>
转换为List<DogPO>
,该怎么办呢。也很简单,只需在接口中写下面这样的方法就好
List<DogPO> voListToPOList(List<DogVO> vo);
测试代码
public static void main(String[] args) {
List<DogVO> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
DogVO dogVO = new DogVO();
dogVO.setId(i);
dogVO.setName("旺财" + i);
dogVO.setWeight(20 + i);
dogVO.setBirthday(new Date());
list.add(dogVO);
}
List<DogPO> dogPOList = DogConvertor.INSTANCE.voListToPOList(list);
System.out.println(dogPOList);
}
属性类型不同
将DogPO的 birthday
改为 String
类型
@Data
public class DogPO {
private int id;
private String dogName;
private int weight;
private String birthday;
}
@Mappings({
@Mapping(source = "name", target = "dogName"),
@Mapping(target = "birthday", expression = "java(cn.hutool.core.date.DateUtil.format(vo.getBirthday(), \"yyyy-MM-dd HH:mm:ss\"))")
})
DogPO voToPO(DogVO vo);
输出结果
多个bean合并为一个bean
在实际业务情况中,我们有时候会遇到将多个Bean转换为一个Bean的情况。我们知道狗子身上一定会有一条尾巴,我们创建一个 TailVO 类
@Data
@AllArgsConstructor
public class TailVO {
private int length;
}
我们要将 TailVO 和 DogVO 合并为一个 DogPO ,在DogPO类中添加一个tailLength
属性
@Data
public class DogPO {
private int id;
private String dogName;
private int weight;
private String birthday;
/**
* 尾巴长度
*/
private int tailLength;
}
@Mappings({
@Mapping(source = "vo.name", target = "dogName"), //由于多了一个参数,所以这里的必须要指定vo.name
@Mapping(target = "birthday", expression = "java(cn.hutool.core.date.DateUtil.format(vo.getBirthday(), \"yyyy-MM-dd HH:mm:ss\"))"),
@Mapping(source = "tailVO.length", target = "tailLength")
})
DogPO voToPO(DogVO vo, TailVO tailVO);
测试代码
public static void main(String[] args) {
DogVO dogVO = new DogVO();
dogVO.setId(1);
dogVO.setName("旺财");
dogVO.setWeight(20);
dogVO.setBirthday(new Date());
TailVO tailVO = new TailVO(10);
DogPO dogPO = DogConvertor.INSTANCE.voToPO(dogVO, tailVO);
System.out.println(dogPO);
}
输出结果
以上就是我在项目中使用MapStruct常用的一些用法,当然它本身还有很多高级的用法,大家可以自行去探索