mapStruct笔记

背景

mapStruct 是一个方便对象转换的工具,类似的工具还有 Dozer, BeanUtils。

实现

mapStruct的核心是在编译期生成基于转换规则的 Impl 文件,运行时直接调用 Impl 文件中的函数。整个 mapStruct 分成三个部分:

  1. 自定义注解,指定转换的规则。例如 source, target 等。

  2. freemarker 模板,用来生成 impl 文件。

  3. 基于 javax.annotation.processing 的处理模块。

基本流程是

解析注解
生成 Mapper Model
将 Model 按规则写入 Freemarker 模板中, 并生成 Impl 文件
生成Impl对象, 转换时调用

具体解析

具体的解析逻辑是将解析注解内容转化为 Mapper model 对象,然后将 Mapper model 写入 freemarker 模板中。

处理框架

整个注解的解析是通过 java compile[1] 实现的,逻辑包含在MappingProcessor.process 函数中,并通过 MapperGenerationVisitor 进行解析。

  @Override
    public boolean process(
            final Set<? extends TypeElement> annotations,
            final RoundEnvironment roundEnvironment) {
        // 遍历需要处理的注解
        for ( TypeElement oneAnnotation : annotations ) {

            //Indicates that the annotation's type isn't on the class path of the compiled
            //project. Let the compiler deal with that and print an appropriate error.
            if ( oneAnnotation.getKind() != ElementKind.ANNOTATION_TYPE ) {
                continue;
            }
// 遍历包含 Mapper 注解的 interface and class , 例如 org.mapstruct.ap.test.conversion.SourceTargetMapper
            for ( Element oneAnnotatedElement : roundEnvironment.getElementsAnnotatedWith( oneAnnotation ) ) {
                // MapperGenerationVisitor 解析每个Mapper 注解的内容 成为一个 Model
                oneAnnotatedElement.accept( new MapperGenerationVisitor( processingEnv ), null );
            }
        }

        return ANNOTATIONS_CLAIMED_EXCLUSIVELY;
    }

解析逻辑

MapperGenerationVisitor 负责解析注解为 Mapper model, 并写入 ftl 模板文件中。

MapperGenerationVisitor.retrieveModel 包含了具体的解析逻辑,将注解内容转化为 Mapper Model。

ModelWriter 负责将 Mapper Model 写入 ftl 模板中。

整个逻辑都是围绕 Mapper model 展开的, Mapper 包含如下内容:

  private final String packageName; // 包的名称

    private final String interfaceName; // 接口名称

    private final String implementationName; // 应用名称

    private final List<BeanMapping> beanMappings; // 一系列的 mapping 信息, 每个 method 对应一个 BeanMapping

每一个 BeanMapping 对应一个转换函数,它的格式如下:


    private final Type sourceType; // 函数的输入参数类型
    private final Type targetType; // 函数的结果参数类型
    private final List<PropertyMapping> propertyMappings; // 转换函数的每个属性的信息
    private final MappingMethod mappingMethod; // 映射的函数
    private final MappingMethod reverseMappingMethod; // 翻转映射的函数
    private final boolean isIterableMapping; // 是不是迭代

例如 SourceTargetMapper 接口:


@Mapper
public interface SourceTargetMapper {

    public static SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );

    @Mappings({
            @Mapping(source = "qax", target = "baz"),
            @Mapping(source = "baz", target = "qax")
    })
    Target sourceToTarget(Source source);

    Source targetToSource(Target target);
}

映射为 Mapper Model 为:

{
    "beanMappings":[
        {
            "iterableMapping":false,
            "mappingMethod":{
                "name":"sourceToTarget",
                "parameterName":"source"
            },
            "propertyMappings":[
                {
                    "fromConversion":"target.getFoo().intValue()",
                    "sourceName":"foo",
                    "sourceType":{
                        "name":"int",
                        "primitive":true
                    },
                    "targetName":"foo",
                    "targetType":{
                        "name":"Long",
                        "packageName":"java.lang",
                        "primitive":false
                    },
                    "toConversion":"Long.valueOf( source.getFoo() )"
                },
                Object{...},
                Object{...},
                Object{...},
                Object{...}
            ],
            "reverseMappingMethod":{
                "name":"targetToSource",
                "parameterName":"target"
            },
            "sourceType":{
                "name":"Source",
                "packageName":"org.mapstruct.ap.test.conversion",
                "primitive":false
            },
            "targetType":{
                "name":"Target",
                "packageName":"org.mapstruct.ap.test.conversion",
                "primitive":false
            }
        }
    ],
    "implementationName":"SourceTargetMapperImpl",
    "interfaceName":"SourceTargetMapper",
    "packageName":"org.mapstruct.ap.test.conversion"
}

写入模板

写入模板是使用 freemarker 进行编写的,最初写入逻辑很简单,直接使用 ModelWriter 进行写入。ftl 模板的部分内容如下:


package ${packageName};

import java.util.ArrayList;
import java.util.List;

public class ${implementationName} implements ${interfaceName} {

上面的 ${packageName}对应的就是 Mapper Model 中的 packageName。

参考

  1. javax.lang.model.element.Element
  2. 编译器 API

原文地址:https://www.cnblogs.com/SpeakSoftlyLove/p/9794661.html

背景

mapStruct 是一个方便对象转换的工具,类似的工具还有 Dozer, BeanUtils。

实现

mapStruct的核心是在编译期生成基于转换规则的 Impl 文件,运行时直接调用 Impl 文件中的函数。整个 mapStruct 分成三个部分:

  1. 自定义注解,指定转换的规则。例如 source, target 等。

  2. freemarker 模板,用来生成 impl 文件。

  3. 基于 javax.annotation.processing 的处理模块。

基本流程是

解析注解
生成 Mapper Model
将 Model 按规则写入 Freemarker 模板中, 并生成 Impl 文件
生成Impl对象, 转换时调用

具体解析

具体的解析逻辑是将解析注解内容转化为 Mapper model 对象,然后将 Mapper model 写入 freemarker 模板中。

处理框架

整个注解的解析是通过 java compile[1] 实现的,逻辑包含在MappingProcessor.process 函数中,并通过 MapperGenerationVisitor 进行解析。

  @Override
    public boolean process(
            final Set<? extends TypeElement> annotations,
            final RoundEnvironment roundEnvironment) {
        // 遍历需要处理的注解
        for ( TypeElement oneAnnotation : annotations ) {

            //Indicates that the annotation's type isn't on the class path of the compiled
            //project. Let the compiler deal with that and print an appropriate error.
            if ( oneAnnotation.getKind() != ElementKind.ANNOTATION_TYPE ) {
                continue;
            }
// 遍历包含 Mapper 注解的 interface and class , 例如 org.mapstruct.ap.test.conversion.SourceTargetMapper
            for ( Element oneAnnotatedElement : roundEnvironment.getElementsAnnotatedWith( oneAnnotation ) ) {
                // MapperGenerationVisitor 解析每个Mapper 注解的内容 成为一个 Model
                oneAnnotatedElement.accept( new MapperGenerationVisitor( processingEnv ), null );
            }
        }

        return ANNOTATIONS_CLAIMED_EXCLUSIVELY;
    }

解析逻辑

MapperGenerationVisitor 负责解析注解为 Mapper model, 并写入 ftl 模板文件中。

MapperGenerationVisitor.retrieveModel 包含了具体的解析逻辑,将注解内容转化为 Mapper Model。

ModelWriter 负责将 Mapper Model 写入 ftl 模板中。

整个逻辑都是围绕 Mapper model 展开的, Mapper 包含如下内容:

  private final String packageName; // 包的名称

    private final String interfaceName; // 接口名称

    private final String implementationName; // 应用名称

    private final List<BeanMapping> beanMappings; // 一系列的 mapping 信息, 每个 method 对应一个 BeanMapping

每一个 BeanMapping 对应一个转换函数,它的格式如下:


    private final Type sourceType; // 函数的输入参数类型
    private final Type targetType; // 函数的结果参数类型
    private final List<PropertyMapping> propertyMappings; // 转换函数的每个属性的信息
    private final MappingMethod mappingMethod; // 映射的函数
    private final MappingMethod reverseMappingMethod; // 翻转映射的函数
    private final boolean isIterableMapping; // 是不是迭代

例如 SourceTargetMapper 接口:


@Mapper
public interface SourceTargetMapper {

    public static SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );

    @Mappings({
            @Mapping(source = "qax", target = "baz"),
            @Mapping(source = "baz", target = "qax")
    })
    Target sourceToTarget(Source source);

    Source targetToSource(Target target);
}

映射为 Mapper Model 为:

{
    "beanMappings":[
        {
            "iterableMapping":false,
            "mappingMethod":{
                "name":"sourceToTarget",
                "parameterName":"source"
            },
            "propertyMappings":[
                {
                    "fromConversion":"target.getFoo().intValue()",
                    "sourceName":"foo",
                    "sourceType":{
                        "name":"int",
                        "primitive":true
                    },
                    "targetName":"foo",
                    "targetType":{
                        "name":"Long",
                        "packageName":"java.lang",
                        "primitive":false
                    },
                    "toConversion":"Long.valueOf( source.getFoo() )"
                },
                Object{...},
                Object{...},
                Object{...},
                Object{...}
            ],
            "reverseMappingMethod":{
                "name":"targetToSource",
                "parameterName":"target"
            },
            "sourceType":{
                "name":"Source",
                "packageName":"org.mapstruct.ap.test.conversion",
                "primitive":false
            },
            "targetType":{
                "name":"Target",
                "packageName":"org.mapstruct.ap.test.conversion",
                "primitive":false
            }
        }
    ],
    "implementationName":"SourceTargetMapperImpl",
    "interfaceName":"SourceTargetMapper",
    "packageName":"org.mapstruct.ap.test.conversion"
}

写入模板

写入模板是使用 freemarker 进行编写的,最初写入逻辑很简单,直接使用 ModelWriter 进行写入。ftl 模板的部分内容如下:


package ${packageName};

import java.util.ArrayList;
import java.util.List;

public class ${implementationName} implements ${interfaceName} {

上面的 ${packageName}对应的就是 Mapper Model 中的 packageName。

参考

  1. javax.lang.model.element.Element
  2. 编译器 API

猜你喜欢

转载自www.cnblogs.com/jpfss/p/10899238.html