由于Dozer不再维护,公司内部的项目需要从Dozer转移到MapStruct,自学了一下MapStruct的使用方法。
MapStruct简介
MapStruct与传统的复制 JavaBean 的工具不同(比如Dozer),并不提供JavaBean转换的方法,他只是个代码生成的工具,会自动将JavaBean转换的方法生成到本地,从而在代码中直接调用生成的方法,避免了反射的使用,一定程度上提高了性能
MapStruct是编译时的工具,与Dozer等运行时的工具相比,在程序运行时不会调用MapStruct本身,而是使用MapStruct编译时生成的代码,从而可以进行debug,并且由于是编译时就已经生成的代码,可以在编译时发现错误。
但是与Dozer等相比,MapStruct需要自动生成代码,是编译时的工具,需要在编译时就指定转换前和转换后的Bean的类的类型,不能像Dozer等一样的使用泛型和Object来实现任意类型的Bean转换
在Eclipse中使用MapStruct
介绍在Eclipse中的Maven工程里使用MapStruct
使用其他工具可以参照官网教程 https://mapstruct.org/documentation/installation/
1.添加依赖
在pom.xml中加入以下代码
...
<properties>
<org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
</properties>
...
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
</dependencies>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source> <!-- depending on your project -->
<target>1.8</target> <!-- depending on your project -->
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<!-- other annotation processors -->
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
2.安装自动处理注解的工具(Eclipse - m2e-apt)
如果不安装,将不会自动生成代码,可以通过mvn命令手动编译来生成本地代码,但是这样不会实时修改生成的代码,建议安装m2e-apt
安装方法:打开Eclipse - Help - Eclipse Marketplace
搜索m2e-apt 按下Install
安装完后在工程的pom.xml的properties继续中加入以下代码
<properties>
...
<!-- automatically run annotation processors within the incremental compilation -->
<m2e.apt.activation>jdt_apt</m2e.apt.activation>
...
</properties>
示例
经过上述步骤已经可以在工程内使用MapStruct了
1.引入MapStruct 首先要创建一个MapStruct的接口
创建一个接口,并加上@Mapper注解,这样一个MapStruct接口就做好了
import org.mapstruct.Mapper;
@Mapper
public interface MyMapStruct {
}
查看target/generated-sources/annotations目录下,生成了接口的实现类
打开可以看到自动生成的代码
2.接下来定义需要转换的Bean类
public class DTO01 {
private String field01;
private String field02;
public String getField01() {
return field01;
}
public void setField01(String field01) {
this.field01 = field01;
}
public String getField02() {
return field02;
}
public void setField02(String field02) {
this.field02 = field02;
}
}
public class DTO02 {
private String field01;
private String field02;
public String getField01() {
return field01;
}
public void setField01(String field01) {
this.field01 = field01;
}
public String getField02() {
return field02;
}
public void setField02(String field02) {
this.field02 = field02;
}
}
3.在接口中定义转换方法
只需要定义方法的返回类型 名字和参数即可,MapStruct会自动生成实现方法
方法名是任意的,自己起名即可
import org.mapstruct.Mapper;
@Mapper
public interface MyMapStruct {
DTO01 DTO02_To_DTO01(DTO02 dto02);
}
查看target/generated-sources/annotations目录下的实现类
MapStruct自动生成了转换方法,实际上也是基于set get方法来实现的,简单易读
4.调用方法
首先实例化对象,然后像普通方法一样通过对象调用即可
实例化方法
MyMapStruct myMapStruct = Mappers.getMapper(MyMapStruct.class);
如果使用Spring等框架来管理bean,也可以使用Inject注解来实例化
@Inject
MyMapStruct myMapStruct;
测试:
import org.mapstruct.factory.Mappers;
public class MapStructTest {
public static void main(String[] args) {
DTO02 dto02 = new DTO02();
dto02.setField01("01");
dto02.setField02("02");
//实例化
MyMapStruct myMapStruct = Mappers.getMapper(MyMapStruct.class);
DTO01 dto01 = myMapStruct.DTO02_To_DTO01(dto02);
System.out.println(dto01.getField01());
System.out.println(dto01.getField02());
}
}
成功将DTO02的值复制给了DTO01
5.自定义转换方法
如果不想使用MapStruct自动生成的方法也可以在接口中自定义
可以使用default 关键字,并自己添加方法体来实现,必须要return
import org.mapstruct.Mapper;
@Mapper
public interface MyMapStruct {
DTO01 DTO02_To_DTO01(DTO02 dto02);
default DTO01 MyMethod(DTO02 dto02){
DTO01 dto01 = new DTO01();
dto01.setField01(dto02.getField01() + "my1");
dto01.setField02(dto02.getField02() + "my2");
return dto01;
}
}
6.自定义某个字段的转换
如果Bean中的字段需要特殊的转换,也可以自己来实现
例如
将DTO01中的field01 修改为boolean类型
DTO02中的field01 修改为 int类型
public class DTO01 {
private boolean field01;
private String field02;
public boolean isField01() {
return field01;
}
public void setField01(boolean field01) {
this.field01 = field01;
}
public String getField02() {
return field02;
}
public void setField02(String field02) {
this.field02 = field02;
}
}
public class DTO02 {
private int field01;
private String field02;
public int getField01() {
return field01;
}
public void setField01(int field01) {
this.field01 = field01;
}
public String getField02() {
return field02;
}
public void setField02(String field02) {
this.field02 = field02;
}
}
这时候接口在报错
Can’t map property “int field01” to “boolean field01”. Consider to declare/implement a mapping method: “boolean map(int value)”.
由于int无法转换成boolean ,mapStruct无法自动生成代码,所以需要自己来定义该字段的转换
首先定义自己的转换逻辑方法,并加上@Named注解,定义一个别名
注意:是这个包下的Named org.mapstruct.Named
@Named("toBoolean")
default boolean intToBoolean(int value) {
if (value >0) {
return true;
}else {
return false;
}
}
然后在转换方法上使用@Mapping注解,target = 需要转换的字段名 qualifiedByName = 自定义的转换方法别名(@Named上定义的)
@Mapping(target = "field01", qualifiedByName = "toBoolean")
DTO01 DTO02_To_DTO01(DTO02 dto02);
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
@Mapper
public interface MyMapStruct {
@Mapping(target = "field01", qualifiedByName = "toBoolean")
DTO01 DTO02_To_DTO01(DTO02 dto02);
@Named("toBoolean")
default boolean intToBoolean(int value) {
if (value >0) {
return true;
}else {
return false;
}
}
}
这样的话两个Bean的field01字段就会按照自定义的逻辑来转换了