引入maven:
org.mapstruct mapstruct 1.4.2.Final
org.mapstruct mapstruct-processor 1.4.2.Final
使用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的注解提示。
以TestMapStruct
接口为例,MapStruct的底层原理是生成一个新类TestMapStructImpl
实现TestMapStruct
接口,并根据规则实现aToB
方法,在aToB
方法中自动根据两个类的字段名生成对应的setter
和getter
方法,最终达到由我们自己编写setter
和getter
方法一样的效果。因此从原理上而言,MapStruct调用时的性能和我们自己编写setter
、getter
方法性能基本无任何差异,这是其它的BeanUtil实现方式无可比拟的。
换句话说,MapStruct的作用就是在编译时为我们自动生成对应的setter
和getter
方法。这样的实现方式效率从本质上就已经超越了各个BeanUtil运行时根据反射动态赋值。
对接Spring框架官方提供了支持,只需要按以下配置即可:
@Mapper(componentModel = "spring")
public interface TestMapStruct {...
}public class Test {@Autowiredprivate TestMapStruct testMapStruct;
}
设置了componentModel="spring"
后生成的实现类将会被@Component
注解注释,并由Spring加载到容器中使用。
如果需要复制属性的两个类存在部分字段名称或类型不一致时,可使用@Mapping
注解来进行手动的映射。
赋值属性,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);
}
支持将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
实现。
支持使用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);
}
赋值属性,不能和其它赋值属性一起使用,配置该属性时会默认为新生成的类设置常量值。如:
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);
}
赋值属性,这个属性决定了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);
}
配置忽略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);
}
被@Context
注解的参数将会作为方法的上下文传递到其它方法中,使用expression
的java语法可对其进行操作。
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 aToB(List classA);/** 不支持 */@Mapping(target = "b", ignore = true);List aToB(List 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 aToB(List classA, @Context String test);
}
假设此时有个场景,需要对某个参数进行转换,但转换逻辑较为复杂,需要调用另一个实现类方法来完成,此时我们就可以使用@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 aToB(List classA, @Context Function test);
}
在调用时则只需要根据函数接口Function
的操作方式传入对应的逻辑即可。@Context
和expression
的组合可实现的功能非常多,这种使用方式只是抛砖引玉,Function
完全也可以传入其它的实现类,在expression
中编写调用方式也能实现。@Context
和expression
组合基本能覆盖开发时的应用场景了。
如果要学习MapStruct的更多使用方式,推荐阅读MapStruct官方文档。
上一篇:20230314 ax
下一篇:MQTT之mosquitto