如何在运行时提供 MapStruct Mapping 注解映射元数据
How to provide MapStruct Mapping annotation mapping meta data at runtime
我们在业务模型和我们的ui-模型之间使用MapStruct。
当 UI 客户端想要获取排序后的数据时,它可以从 ui-model 中指定一个字段。
我们的 MapStructParser
可以获得相应的 business-model 字段名称并创建所需的条件对其进行排序。
示例:
public interface ModelMapping extends BridgeMapping<BusinessModel, UiModel> {
@Mapping(source = "zip", target = "plz")
UiModel modelToUiModel(BusinessModel model, @MappingTarget UiModel uiModel);
}
问题:
如何读取@Mapping(source = "zip", target = "plz")
注解得到source
和target
值?
Mapping
-注释有 RetentionPolicy.CLASS
,因此无法通过反射访问它。
我们使用ASM(字节码操作和分析框架)解决了这个问题
阅读 Mapping
-注释并在元模型中提供它:
示例(还有available on Github)
public class AnnotationParser {
public void parse(Class<?> mapper) {
ClassInfoCollector classPrinter = new ClassInfoCollector(annotationInfos);
ClassReader cr = new ClassReader(mapper.getCanonicalName());
cr.accept(classPrinter, 0);
}
}
public class ClassInfoCollector extends ClassVisitor {
private final List<MethodAnnotationInfo> mapStructParser;
public ClassInfoCollector(List<MethodAnnotationInfo> mapStructParser) {
super(ASMversion);
this.mapStructParser = mapStructParser;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String methodName, String descriptor, String signature, String[] exceptions) {
return new MethodInfoCollector(methodName, mapStructParser);
}
}
public class ClassInfoCollector extends ClassVisitor {
private final List<MethodAnnotationInfo> mapStructParser;
public ClassInfoCollector(List<MethodAnnotationInfo> mapStructParser) {
super(ASMversion);
this.mapStructParser = mapStructParser;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String methodName, String descriptor, String signature, String[] exceptions) {
return new MethodInfoCollector(methodName, mapStructParser);
}
}
class MethodInfoCollector extends MethodVisitor {
private final String methodName;
private final List<MethodAnnotationInfo> mapStructParser;
public MethodInfoCollector(String method, List<MethodAnnotationInfo> mapStructParser) {
super(ASMversion);
this.methodName = method;
this.mapStructParser = mapStructParser;
}
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return new MethodAnnotationInfoCollector(methodName, descriptor, mapStructParser);
}
}
class MethodAnnotationInfoCollector extends AnnotationVisitor {
private final String method;
private final String annotationType;
private final List<MethodAnnotationInfo> mapStructParser;
public MethodAnnotationInfoCollector(String method, String annotationType, List<MethodAnnotationInfo> mapStructParser) {
super(ASMversion);
this.method = method;
this.annotationType = annotationType;
this.mapStructParser = mapStructParser;
}
@Override
public void visit(String name, Object value) {
MethodAnnotationInfo annotationInfo = new MethodAnnotationInfo(method, annotationType, name, value.toString());
mapStructParser.add(annotationInfo);
super.visit(name, value);
}
}
有了这个 AnnotationParser
就可以通过这种方式获取映射信息:
class BusinessModel{
String zip;
}
class UiModel{
String plz;
}
public interface ModelMapping extends BridgeMapping<BusinessModel, UiModel> {
@Mapping(source = "zip", target = "plz")
UiModel modelToUiModel(BusinessModel model, @MappingTarget UiModel uiModel);
}
@Test
public testMappingInfo(){
MapStructParser mappingInfo = new MapStructParser();
mappingInfo.parseMappingInterface(ModelMapping.class);
assertEquals("zip", mappingInfo.mapToTargetField("plz"));
}
mappingInfo.mapToTargetField("plz")
returns if BusinessModel
(zip
).
的映射字段
AnnotationParser
是一个通用注释解析器,它提供 MethodAnnotationInfo
.
的列表
MapStructParser
使用 AnnotationParser
模型通过收集 Mapping
-注释来构建 MapStructMappingInfo
。
可在此处获得完整的可运行和测试示例:
https://github.com/TobseF/mapstruct-metadata-example
Theoretically it also possible to hook into the MapStruct annotation processing process and generate MetaModel Java classes (ModelElementProcessor
). But I wasn't able to get it working. Adding additional Annotation processors is't straight forward and debugging Annotation processing during class compilation is more than cumbersome. Four our purpose the ASM way and a simple mapping is sufficient.
我们在业务模型和我们的ui-模型之间使用MapStruct。
当 UI 客户端想要获取排序后的数据时,它可以从 ui-model 中指定一个字段。
我们的 MapStructParser
可以获得相应的 business-model 字段名称并创建所需的条件对其进行排序。
示例:
public interface ModelMapping extends BridgeMapping<BusinessModel, UiModel> {
@Mapping(source = "zip", target = "plz")
UiModel modelToUiModel(BusinessModel model, @MappingTarget UiModel uiModel);
}
问题:
如何读取@Mapping(source = "zip", target = "plz")
注解得到source
和target
值?
Mapping
-注释有 RetentionPolicy.CLASS
,因此无法通过反射访问它。
我们使用ASM(字节码操作和分析框架)解决了这个问题
阅读 Mapping
-注释并在元模型中提供它:
示例(还有available on Github)
public class AnnotationParser {
public void parse(Class<?> mapper) {
ClassInfoCollector classPrinter = new ClassInfoCollector(annotationInfos);
ClassReader cr = new ClassReader(mapper.getCanonicalName());
cr.accept(classPrinter, 0);
}
}
public class ClassInfoCollector extends ClassVisitor {
private final List<MethodAnnotationInfo> mapStructParser;
public ClassInfoCollector(List<MethodAnnotationInfo> mapStructParser) {
super(ASMversion);
this.mapStructParser = mapStructParser;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String methodName, String descriptor, String signature, String[] exceptions) {
return new MethodInfoCollector(methodName, mapStructParser);
}
}
public class ClassInfoCollector extends ClassVisitor {
private final List<MethodAnnotationInfo> mapStructParser;
public ClassInfoCollector(List<MethodAnnotationInfo> mapStructParser) {
super(ASMversion);
this.mapStructParser = mapStructParser;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String methodName, String descriptor, String signature, String[] exceptions) {
return new MethodInfoCollector(methodName, mapStructParser);
}
}
class MethodInfoCollector extends MethodVisitor {
private final String methodName;
private final List<MethodAnnotationInfo> mapStructParser;
public MethodInfoCollector(String method, List<MethodAnnotationInfo> mapStructParser) {
super(ASMversion);
this.methodName = method;
this.mapStructParser = mapStructParser;
}
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return new MethodAnnotationInfoCollector(methodName, descriptor, mapStructParser);
}
}
class MethodAnnotationInfoCollector extends AnnotationVisitor {
private final String method;
private final String annotationType;
private final List<MethodAnnotationInfo> mapStructParser;
public MethodAnnotationInfoCollector(String method, String annotationType, List<MethodAnnotationInfo> mapStructParser) {
super(ASMversion);
this.method = method;
this.annotationType = annotationType;
this.mapStructParser = mapStructParser;
}
@Override
public void visit(String name, Object value) {
MethodAnnotationInfo annotationInfo = new MethodAnnotationInfo(method, annotationType, name, value.toString());
mapStructParser.add(annotationInfo);
super.visit(name, value);
}
}
有了这个 AnnotationParser
就可以通过这种方式获取映射信息:
class BusinessModel{
String zip;
}
class UiModel{
String plz;
}
public interface ModelMapping extends BridgeMapping<BusinessModel, UiModel> {
@Mapping(source = "zip", target = "plz")
UiModel modelToUiModel(BusinessModel model, @MappingTarget UiModel uiModel);
}
@Test
public testMappingInfo(){
MapStructParser mappingInfo = new MapStructParser();
mappingInfo.parseMappingInterface(ModelMapping.class);
assertEquals("zip", mappingInfo.mapToTargetField("plz"));
}
mappingInfo.mapToTargetField("plz")
returns if BusinessModel
(zip
).
的映射字段
AnnotationParser
是一个通用注释解析器,它提供 MethodAnnotationInfo
.
的列表
MapStructParser
使用 AnnotationParser
模型通过收集 Mapping
-注释来构建 MapStructMappingInfo
。
可在此处获得完整的可运行和测试示例:
https://github.com/TobseF/mapstruct-metadata-example
Theoretically it also possible to hook into the MapStruct annotation processing process and generate MetaModel Java classes (
ModelElementProcessor
). But I wasn't able to get it working. Adding additional Annotation processors is't straight forward and debugging Annotation processing during class compilation is more than cumbersome. Four our purpose the ASM way and a simple mapping is sufficient.