文章目录
- 1. MapStruct使用方式
- 2. 为什么选择MapStruct
- 3. 使用说明
- 3.1 对接Spring框架
- 3.2 @Mapping的使用
- 3.2.1 target和source
- 3.2.2 dateFormat属性
- 3.2.3 numberFormat属性
- 3.2.4 constant属性
- 3.2.5 expression属性
- 3.2.6 ignore属性
- 3.3 @Context的使用
- 3.3.1 集合传递自定义参数
- 3.3.2 函数式编程实现自定义功能
- 3.4 Map转对象
1. MapStruct使用方式
引入maven:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.3.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.3.Final</version>
</dependency>
使用mapstruct-jdk8
编译可能会报java: Couldn't retrieve @Mapper annotation
错误,替换成mapstruct
即可。
MapStruct使用方式十分简单,创建一个interface类,并编写转换方法:
@Mapper
public interface TestMapStruct {
TestMapStruct INSTANCE = Mappers.getMapper(TestMapStruct.class);
ClassB aToB(ClassA a);
}
假设ClassA
和ClassB
的字段名称一模一样。使用时直接调用方法即可:
ClassB b = TestMapStruct.INSTANCE.aToB(a);
这样就能完成不同类相同字段的属性映射,十分简单。
IDEA推荐下载MapStruct Support
插件,以方便看到MapStruct的注解提示。
2. 为什么选择MapStruct
以TestMapStruct
接口为例,MapStruct的底层原理是生成一个新类TestMapStructImpl
实现TestMapStruct
接口,并根据规则实现aToB
方法,在aToB
方法中自动根据两个类的字段名生成对应的setter
和getter
方法,最终达到由我们自己编写setter
和getter
方法一样的效果。因此从原理上而言,MapStruct调用时的性能和我们自己编写setter
、getter
方法性能基本无任何差异,这是其它的BeanUtil实现方式无可比拟的。
换句话说,MapStruct的作用就是在编译时为我们自动生成对应的setter
和getter
方法。这样的实现方式效率从本质上就已经超越了各个BeanUtil运行时根据反射动态赋值。
3. 使用说明
3.1 对接Spring框架
对接Spring框架官方提供了支持,只需要按以下配置即可:
@Mapper(componentModel = "spring")
public interface TestMapStruct {
...
}
public class Test {
@Autowired
private TestMapStruct testMapStruct;
}
设置了componentModel="spring"
后生成的实现类将会被@Component
注解注释,并由Spring加载到容器中使用。
3.2 @Mapping的使用
如果需要复制属性的两个类存在部分字段名称或类型不一致时,可使用@Mapping
注解来进行手动的映射。
3.2.1 target和source
赋值属性,target为目标字段,source为来源字段,target一定不能为空。名称不一致时可指定target和source名称,如:
public class ClassA {
private String a;
}
public class ClassB {
private String b;
}
@Mapper(componentModel = "spring")
public interface TestMapStruct {
@Mapping(target = "b", source = "a");
ClassB aToB(ClassA classA);
}
3.2.2 dateFormat属性
支持将Date
属性以日期格式转成String
。
返回给前端的Vo类有createTime
属性,为String
类型,但数据库实体类型为Date
,此时可以使用该属性将Date
自动以某种格式转成String
,如:
public class ClassA {
private Date a;
}
public class ClassB {
private String b;
}
@Mapper(componentModel = "spring")
public interface TestMapStruct {
@Mapping(target = "b", source = "a", dateFormat = "yyyy");
ClassB aToB(ClassA classA);
}
dateFormat
使用SimpleDateFormat
实现。
3.2.3 numberFormat属性
支持使用DecimalFormat
方式将数字转换成String
。如:
public class ClassA {
private double a;
}
public class ClassB {
private String b;
}
@Mapper(componentModel = "spring")
public interface TestMapStruct {
@Mapping(target = "b", source = "a", numberFormat = "#.##元");
ClassB aToB(ClassA classA);
}
3.2.4 constant属性
赋值属性,不能和其它赋值属性一起使用,配置该属性时会默认为新生成的类设置常量值。如:
public class ClassA {
private String a;
}
public class ClassB {
private String b;
}
@Mapper(componentModel = "spring")
public interface TestMapStruct {
@Mapping(target = "b", constant = "这是B属性");
ClassB aToB(ClassA classA);
}
3.2.5 expression属性
赋值属性,这个属性决定了MapStruct的可扩展性,不能和其它赋值一起使用,支持按expression = "java(java语句)"
的方式来设置字段需要执行的java语句。如:
public class ClassA {
private String a;
}
public class ClassB {
private String b;
}
@Mapper(componentModel = "spring")
public interface TestMapStruct {
@Mapping(target = "b", constant = "java()");
ClassB aToB(ClassA classA);
}
3.2.6 ignore属性
配置忽略target属性,如:
public class ClassA {
private String a;
}
public class ClassB {
private String b;
}
@Mapper(componentModel = "spring")
public interface TestMapStruct {
@Mapping(target = "b", ignore = true);
ClassB aToB(ClassA classA);
}
3.3 @Context的使用
被@Context
注解的参数将会作为方法的上下文传递到其它方法中,使用expression
的java语法可对其进行操作。
3.3.1 集合传递自定义参数
MapStruct不支持集合映射时直接传递自定义参数,如下:
public class ClassA {
private String a;
private String test;
}
public class ClassB {
private String b;
private String test;
}
@Mapper(componentModel = "spring")
public interface TestMapStruct {
/** 支持 */
@Mapping(target = "b", ignore = true);
List<ClassB> aToB(List<ClassA> classA);
/** 不支持 */
@Mapping(target = "b", ignore = true);
List<ClassB> aToB(List<ClassA> classA, String test);
}
上面的的方式在编译时会报错。此时可以使用@Context
注解来实现,如下:
public class ClassA {
private String a;
private String test;
}
public class ClassB {
private String b;
private String test;
}
@Mapper(componentModel = "spring")
public interface TestMapStruct {
@Mapping(target = "b", ignore = true);
@Mapping(target = "test", expression = "java(test)");
List<ClassB> aToB(List<ClassA> classA, @Context String test);
}
3.3.2 函数式编程实现自定义功能
假设此时有个场景,需要对某个参数进行转换,但转换逻辑较为复杂,需要调用另一个实现类方法来完成,此时我们就可以使用@Context
+expression
来实现这种复杂的功能。如下:
public class ClassA {
private String a;
private String test;
}
public class ClassB {
private String b;
private String test;
}
@Mapper(componentModel = "spring")
public interface TestMapStruct {
@Mapping(target = "b", ignore = true);
@Mapping(target = "test", expression = "java(test.apply(classA))");
List<ClassB> aToB(List<ClassA> classA, @Context Function<String, String> test);
}
在调用时则只需要根据函数接口Function
的操作方式传入对应的逻辑即可。@Context
和expression
的组合可实现的功能非常多,这种使用方式只是抛砖引玉,Function
完全也可以传入其它的实现类,在expression
中编写调用方式也能实现。
3.4 Map转对象
MapStruct从1.5.3.Final版本开始支持Map转普通对象,但不支持对象转Map。如下:
public class ClassB {
private String b;
private String test;
}
@Mapper(componentModel = "spring")
public interface TestMapStruct {
ClassB mapToB(Map<String, Object> map);
List<ClassB> mapToB(List<Map<String, Object>> map);
default String objectToString(Object obj) {
return (String) obj;
}
}
字段一致可以不指定@Mapping
注解,如果不一致则需要指定target和source字段名。MapStruct会识别target和source类型,并识别是否有targetToSource方法,有的话则进行调用,也可以使用静态方法写在其它的类,使用@Mapper
注解中的uses
属性引入。如下:
public class TestMapper {
/** 需要为静态方法,如果不为静态方法TestMapper则必须被Spring管理 */
public static String objectToString(Object obj) {
return (String) obj;
}
}
public class ClassB {
private String b;
private String test;
}
@Mapper(componentModel = "spring", uses = TestMapper.class)
public interface TestMapStruct {
ClassB mapToB(Map<String, Object> map);
List<ClassB> mapToB(List<Map<String, Object>> map);
}
上面的这种方式也能保证识别到Object转String,并调用objectToString
方法。如果TestMapper
类的方法不是静态方法,则TestMapper
需要和TestMapStruct
注入方式保持一直。如TestMapStruct
给Spring管理,则TestMapper
也必须被Spring管理。