系统设计五-JavaBean转换利器MapStruct

MapStruct是一个基于Java注解的实体类映射框架,它在编译期间生成转换代码,提高性能和安全性。相比BeanUtils等运行时映射工具,MapStruct具有更快的速度和更少的错误。本文介绍了MapStruct的基本用法,包括如何引入依赖、定义映射接口、使用注解以及其实现原理,并提供了自定义注解处理器的示例。通过MapStruct,可以简化Java对象间的转换,提高代码质量。
摘要由CSDN通过智能技术生成

MapStruct介绍

  mapstruct是一种实体类映射框架,能够通过Java注解将一个实体类的属性安全地赋值给另一个实体类。有了mapstruct,只需要定义一个映射器接口,声明需要映射的方法,在编译过程中,mapstruct会自动生成该接口的实现类,实现将源对象映射到目标对象的效果。

其他映射框架对比

 实体类映射框架大致有两种:一种是运行期通过java反射机制动态映射,如使用BeanUtils, BeanCopier 等工具做JavaBean对象转换;另一种是编译期动态生成getter/setter,在运行期直接调用框架编译好的class类实现实体映射。      

        由于mapstruct映射是在编译期间实现的,因此相比运行期的映射框架有以下几个优点:

  1. 安全性高。因为是编译期就实现源对象到目标对象的映射, 如果编译器能够通过,运行期就不会报错。
  2. 速度快。速度快指的是运行期间直接调用实现类的方法,不会在运行期间使用反射进行转化。   

Mapstruct 入门

maven坐标地址

   首先我们要引入mapstruct 的jar 包,它的maven 坐标地址如下:

       <!--mapStruct依赖 自动生成VO-DO的模型映射-->
        <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>
            <scope>provided</scope>
        </dependency>

Start  Mapstruct 

   mapstruct的用法很简单,假设我们有两个实体类BookCategoryDO和BookCategoryDTO,类定义如下:

  • BookCategoryDO 定义
@Data
@TableName("book_category")
public class BookCategoryDO {
    private Integer id;

    /**
     * 分类名称
     */
    private String categoryName;

    /**
     * 书籍频道 1-男频;2-女频
     */
    private Integer channelType;

    /**
     * 排序
     */
    private Integer sort;

    /**
     * 状态(0启用,1停用)
     */
    private Integer status;

    /**
     * 创建者
     */
    private String creator;

    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 最后编辑人
     */
    private String updater;

    /**
     * 最后修改时间
     */
    private Date updateTime;

    /**
     * 产品类型
     */
    private Integer productType;

    /**
     * 父节点ID
     */
    private Integer parentId;
}
  • BookCategoryDTO 定义
/**
 * 书籍类别定义
 */
@Data
public class BookCategoryDTO implements DTO {
    private Integer id;

    /**
     * 分类名称
     */
    private String categoryName;

    /**
     * 书籍频道 1-男频;2-女频
     */
    private Integer channelType;

    /**
     * 排序
     */
    private Integer sort;


    /**
     * 产品类型
     */
    private Integer productType;

    /**
     * 父节点ID
     */
    private Integer parentId;
}
  • Assembler<T, R> 转换接口
public interface Assembler<T, R> extends Function<T, R> {
    List<R> toList(List<T> var1);
}
  • 然后需要定义一个用例映射的接口BookCategoryDTOAssembler,接口如下:     
/**
 * 书籍类别转换器
 */
@Mapper(componentModel = "spring")
public interface BookCategoryDTOAssembler extends Assembler<BookCategoryDO, BookCategoryDTO> {
}
  • 定义单元测试MapstructTest
    public class MapstructTest extends BaseTest {
        @Resource
        private BookCategoryDTOAssembler bookCategoryDTOAssembler;
    
        @Test
        public void toBookCategoryDTO() {
            BookCategoryDO bookCategoryDO = new BookCategoryDO();
            bookCategoryDO.setId(1);
            bookCategoryDO.setCategoryName("都市");
            bookCategoryDO.setParentId(0);
            bookCategoryDO.setProductType(1);
    
            BookCategoryDTO dto = bookCategoryDTOAssembler.apply(bookCategoryDO);
    
            System.out.println(dto);
        }
    }

    运行结果:BookCategoryDTO(id=1, categoryName=都市, channelType=null, sort=null, productType=1, parentId=0)

Mapper 获取

  • 通过 Mapper 工厂获取

       通过 Mappers.getMapper(xxx.class) 的方式来进行对应 Mapper 的获取。此种方法为通过 Mapper 工厂获取。@Mapping——一对映射关系

名称描述
target目标属性,赋值的过程是把“源属性”赋值给“目标属性”
source源属性,赋值的过程是把“源属性”赋值给“目标属性”
dateFormat用于源属性是Date,转化为String
numberFormat用户数值类型与String类型之间的转化
constant不管源属性,直接将“目标属性”置为常亮
expression使用表达式进行属性之间的转化
ignore忽略某个属性的赋值;
qualifiedByName根据自定义的方法进行赋值;
defaultValue默认值;

 expression 的使用

@Mapping(expression = "java(null == MyObjectDetailMapper.getLastOne(myObject) ? null : MyObjectDetailMapper.getLastOne(myObject).getNumber())", target = "number"),
   
/**
 * 书籍推请求转换
 *
 */
@Mapper(componentModel = "spring",imports = UUID.class)
public interface BookRecommendQueryAssembler extends Function<BookPopularityPageQuery, BookRecommendReq> {
    @Mappings({
            @Mapping(target = "reqId", expression = "java(UUID.randomUUID().toString())")
    })
    BookRecommendReq apply(BookPopularityPageQuery query);
}

constant 使用

/**
 * 用户书架转换类
 */
@Mapper(componentModel = "spring", imports = {ChannelTypeEnum.class, BookSerialStatusEnum.class})
public interface UserBookShelfDTOAssembler extends Assembler<BookEs, UserBookShelfDTO> {

    @Mappings({
            @Mapping(target = "bookId", source = "id"),
            @Mapping(target = "readingProgress", constant = "未读"),
            @Mapping(target = "channelName", expression = "java(ChannelTypeEnum.getTitleByValue(bookEs.getChannelType()))"),
            @Mapping(target = "serialStatusName", expression = "java(BookSerialStatusEnum.getTitleByValue(bookEs.getSerialStatus()))"),
            @Mapping(target = "twoCategory.id", source = "bookEs.twoCategoryId"),
            @Mapping(target = "twoCategory.categoryName", source = "bookEs.twoCategoryName"),
            @Mapping(target = "threeCategory.id", source = "bookEs.threeCategoryId"),
            @Mapping(target = "threeCategory.categoryName", source = "bookEs.threeCategoryName")
    })
    UserBookShelfDTO apply(BookEs bookEs);

  • 使用依赖注入

          对于 Web 开发,依赖注入应该很熟悉。MapSturct 也支持使用依赖注入,同时也推荐使用依赖注入。componentModel就是依赖注入,类似于在spring的servie层用@servie注入,那么在其他地方可以使用@Autowired取到值。该属性可取的值为

componentModel描述
default这个就是经常使用的 xxxMapper.INSTANCE.xxx
cdi使用该属性,则在其他地方可以使用@Inject取到值
spring使用该属性,则在其他地方可以使用@Autowired取到值
jsr330/Singleton使用者两个属性,可以再其他地方使用@Inject取到值

 Mapstruct实现原理

生成的字节码

查看target下Mapstruct生成的字节码文件BookCategoryDTOAssemblerImpl.class,它实现接口BookCategoryDTOAssembler。BookCategoryDTOAssemblerImpl源代码:

@Component
public class BookCategoryDTOAssemblerImpl implements BookCategoryDTOAssembler {
    public BookCategoryDTOAssemblerImpl() {
    }

    public BookCategoryDTO apply(BookCategoryDO arg0) {
        if (arg0 == null) {
            return null;
        } else {
            BookCategoryDTO bookCategoryDTO = new BookCategoryDTO();
            bookCategoryDTO.setId(arg0.getId());
            bookCategoryDTO.setCategoryName(arg0.getCategoryName());
            bookCategoryDTO.setChannelType(arg0.getChannelType());
            bookCategoryDTO.setSort(arg0.getSort());
            bookCategoryDTO.setProductType(arg0.getProductType());
            bookCategoryDTO.setParentId(arg0.getParentId());
            return bookCategoryDTO;
        }
    }

    public List<BookCategoryDTO> toList(List<BookCategoryDO> arg0) {
        if (arg0 == null) {
            return null;
        } else {
            List<BookCategoryDTO> list = new ArrayList(arg0.size());
            Iterator var3 = arg0.iterator();

            while(var3.hasNext()) {
                BookCategoryDO bookCategoryDO = (BookCategoryDO)var3.next();
                list.add(this.apply(bookCategoryDO));
            }

            return list;
        }
    }
}

MapStruct优点

  1. 性能高:这是相对反射来说的,反射需要去读取字节码的内容,花销会比较大。而通过 MapStruct 来生成的代码,其类似于人手写。速度上可以得到保证。
  2. 使用简单:如果是完全映射的,使用起来肯定没有反射简单。用类似 BeanUtils 这些工具一条语句就搞定了。但是,如果需要进行特殊的匹配(特殊类型转换,多对一转换等),其相对来说也是比较简单的。基本上,使用的时候,我们只需要声明一个接口,接口下写对应的方法,就可以使用了。

Java程序编译流程

 Java源码到class文件的过程其实是一个比较复杂的过程。其中的经过可以用下图描述

com.sun.tools.javac.main.JavaCompiler#compile

 public void compile(List<JavaFileObject> var1, List<String> var2, Iterable<? extends Processor> var3) {
        if (var3 != null && var3.iterator().hasNext()) {
            this.explicitAnnotationProcessingRequested = true;
        }

        if (this.hasBeenUsed) {
            throw new AssertionError("attempt to reuse JavaCompiler");
        } else {
            this.hasBeenUsed = true;
            this.options.put(Option.XLINT_CUSTOM.text + "-" + LintCategory.OPTIONS.option, "true");
            this.options.remove(Option.XLINT_CUSTOM.text + LintCategory.OPTIONS.option);
            this.start_msec = now();

            try {
                this.initProcessAnnotations(var3);
                this.delegateCompiler = this.processAnnotations(this.enterTrees(this.stopIfError(CompileState.PARSE, this.parseFiles(var1))), var2);
                this.delegateCompiler.compile2();
                this.delegateCompiler.close();
                this.elapsed_msec = this.delegateCompiler.elapsed_msec;
            } catch (Abort var8) {
                if (this.devVerbose) {
                    var8.printStackTrace(System.err);
                }
            } finally {
                if (this.procEnvImpl != null) {
                    this.procEnvImpl.close();
                }

            }

        }
    }

org.mapstruct.ap.MappingProcessor

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.mapstruct.ap;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementKindVisitor6;
import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.internal.gem.MapperGem;
import org.mapstruct.ap.internal.gem.ReportingPolicyGem;
import org.mapstruct.ap.internal.option.Options;
import org.mapstruct.ap.internal.processor.DefaultModelElementProcessorContext;
import org.mapstruct.ap.internal.processor.ModelElementProcessor;
import org.mapstruct.ap.internal.util.AnnotationProcessingException;
import org.mapstruct.ap.internal.util.AnnotationProcessorContext;
import org.mapstruct.ap.internal.util.RoundContext;
import org.mapstruct.ap.spi.TypeHierarchyErroneousException;

@SupportedAnnotationTypes({"org.mapstruct.Mapper"})
@SupportedOptions({"mapstruct.suppressGeneratorTimestamp", "mapstruct.suppressGeneratorVersionInfoComment", "mapstruct.unmappedTargetPolicy", "mapstruct.unmappedSourcePolicy", "mapstruct.defaultComponentModel", "mapstruct.defaultInjectionStrategy", "mapstruct.disableBuilders", "mapstruct.verbose"})
public class MappingProcessor extends AbstractProcessor {
    private static final boolean ANNOTATIONS_CLAIMED_EXCLUSIVELY = false;
    protected static final String SUPPRESS_GENERATOR_TIMESTAMP = "mapstruct.suppressGeneratorTimestamp";
    protected static final String SUPPRESS_GENERATOR_VERSION_INFO_COMMENT = "mapstruct.suppressGeneratorVersionInfoComment";
    protected static final String UNMAPPED_TARGET_POLICY = "mapstruct.unmappedTargetPolicy";
    protected static final String UNMAPPED_SOURCE_POLICY = "mapstruct.unmappedSourcePolicy";
    protected static final String DEFAULT_COMPONENT_MODEL = "mapstruct.defaultComponentModel";
    protected static final String DEFAULT_INJECTION_STRATEGY = "mapstruct.defaultInjectionStrategy";
    protected static final String ALWAYS_GENERATE_SERVICE_FILE = "mapstruct.alwaysGenerateServicesFile";
    protected static final String DISABLE_BUILDERS = "mapstruct.disableBuilders";
    protected static final String VERBOSE = "mapstruct.verbose";
    private Options options;
    private AnnotationProcessorContext annotationProcessorContext;
    private Set<DeferredMapper> deferredMappers = new HashSet();

    public MappingProcessor() {
    }

    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.options = this.createOptions();
        this.annotationProcessorContext = new AnnotationProcessorContext(processingEnv.getElementUtils(), processingEnv.getTypeUtils(), processingEnv.getMessager(), this.options.isDisableBuilders(), this.options.isVerbose());
    }

    private Options createOptions() {
        String unmappedTargetPolicy = (String)this.processingEnv.getOptions().get("mapstruct.unmappedTargetPolicy");
        String unmappedSourcePolicy = (String)this.processingEnv.getOptions().get("mapstruct.unmappedSourcePolicy");
        return new Options(Boolean.valueOf((String)this.processingEnv.getOptions().get("mapstruct.suppressGeneratorTimestamp")), Boolean.valueOf((String)this.processingEnv.getOptions().get("mapstruct.suppressGeneratorVersionInfoComment")), unmappedTargetPolicy != null ? ReportingPolicyGem.valueOf(unmappedTargetPolicy.toUpperCase()) : null, unmappedSourcePolicy != null ? ReportingPolicyGem.valueOf(unmappedSourcePolicy.toUpperCase()) : null, (String)this.processingEnv.getOptions().get("mapstruct.defaultComponentModel"), (String)this.processingEnv.getOptions().get("mapstruct.defaultInjectionStrategy"), Boolean.valueOf((String)this.processingEnv.getOptions().get("mapstruct.alwaysGenerateServicesFile")), Boolean.valueOf((String)this.processingEnv.getOptions().get("mapstruct.disableBuilders")), Boolean.valueOf((String)this.processingEnv.getOptions().get("mapstruct.verbose")));
    }

    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
        if (!roundEnvironment.processingOver()) {
            RoundContext roundContext = new RoundContext(this.annotationProcessorContext);
            Set<TypeElement> deferredMappers = this.getAndResetDeferredMappers();
            this.processMapperElements(deferredMappers, roundContext);
            Set<TypeElement> mappers = this.getMappers(annotations, roundEnvironment);
            this.processMapperElements(mappers, roundContext);
        } else if (!this.deferredMappers.isEmpty()) {
            Iterator var8 = this.deferredMappers.iterator();

            while(var8.hasNext()) {
                DeferredMapper deferredMapper = (DeferredMapper)var8.next();
                TypeElement deferredMapperElement = deferredMapper.deferredMapperElement;
                Element erroneousElement = deferredMapper.erroneousElement;
                String erroneousElementName;
                if (erroneousElement instanceof QualifiedNameable) {
                    erroneousElementName = ((QualifiedNameable)erroneousElement).getQualifiedName().toString();
                } else {
                    erroneousElementName = erroneousElement != null ? erroneousElement.getSimpleName().toString() : null;
                }

                deferredMapperElement = this.annotationProcessorContext.getElementUtils().getTypeElement(deferredMapperElement.getQualifiedName());
                this.processingEnv.getMessager().printMessage(Kind.ERROR, "No implementation was created for " + deferredMapperElement.getSimpleName() + " due to having a problem in the erroneous element " + erroneousElementName + ". Hint: this often means that some other annotation processor was supposed to process the erroneous element. You can also enable MapStruct verbose mode by setting -Amapstruct.verbose=true as a compilation argument.", deferredMapperElement);
            }
        }

        return false;
    }

    private Set<TypeElement> getAndResetDeferredMappers() {
        Set<TypeElement> deferred = new HashSet(this.deferredMappers.size());
        Iterator var2 = this.deferredMappers.iterator();

        while(var2.hasNext()) {
            DeferredMapper deferredMapper = (DeferredMapper)var2.next();
            TypeElement element = deferredMapper.deferredMapperElement;
            deferred.add(this.processingEnv.getElementUtils().getTypeElement(element.getQualifiedName()));
        }

        this.deferredMappers.clear();
        return deferred;
    }

    private Set<TypeElement> getMappers(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
        Set<TypeElement> mapperTypes = new HashSet();
        Iterator var4 = annotations.iterator();

        while(true) {
            TypeElement annotation;
            do {
                if (!var4.hasNext()) {
                    return mapperTypes;
                }

                annotation = (TypeElement)var4.next();
            } while(annotation.getKind() != ElementKind.ANNOTATION_TYPE);

            try {
                Set<? extends Element> annotatedMappers = roundEnvironment.getElementsAnnotatedWith(annotation);
                Iterator var7 = annotatedMappers.iterator();

                while(var7.hasNext()) {
                    Element mapperElement = (Element)var7.next();
                    TypeElement mapperTypeElement = this.asTypeElement(mapperElement);
                    if (mapperTypeElement != null && MapperGem.instanceOn(mapperTypeElement) != null) {
                        mapperTypes.add(mapperTypeElement);
                    }
                }
            } catch (Throwable var10) {
                this.handleUncaughtError(annotation, var10);
            }
        }
    }

    private void processMapperElements(Set<TypeElement> mapperElements, RoundContext roundContext) {
        Iterator var3 = mapperElements.iterator();

        while(var3.hasNext()) {
            TypeElement mapperElement = (TypeElement)var3.next();

            try {
                List<? extends Element> tst = mapperElement.getEnclosedElements();
                ModelElementProcessor.ProcessorContext context = new DefaultModelElementProcessorContext(this.processingEnv, this.options, roundContext, this.getDeclaredTypesNotToBeImported(mapperElement), mapperElement);
                this.processMapperTypeElement(context, mapperElement);
            } catch (TypeHierarchyErroneousException var8) {
                TypeMirror erroneousType = var8.getType();
                Element erroneousElement = erroneousType != null ? roundContext.getAnnotationProcessorContext().getTypeUtils().asElement(erroneousType) : null;
                if (this.options.isVerbose()) {
                    this.processingEnv.getMessager().printMessage(Kind.NOTE, "MapStruct: referred types not available (yet), deferring mapper: " + mapperElement);
                }

                this.deferredMappers.add(new DeferredMapper(mapperElement, erroneousElement));
            } catch (Throwable var9) {
                this.handleUncaughtError(mapperElement, var9);
                break;
            }
        }

    }

    private Map<String, String> getDeclaredTypesNotToBeImported(TypeElement element) {
        return (Map)element.getEnclosedElements().stream().filter((e) -> {
            return ElementKind.CLASS.equals(e.getKind());
        }).map(Element::getSimpleName).map(Object::toString).collect(Collectors.toMap((k) -> {
            return k;
        }, (v) -> {
            return element.getQualifiedName().toString() + "." + v;
        }));
    }

    private void handleUncaughtError(Element element, Throwable thrown) {
        StringWriter sw = new StringWriter();
        thrown.printStackTrace(new PrintWriter(sw));
        String reportableStacktrace = sw.toString().replace(System.lineSeparator(), "  ");
        this.processingEnv.getMessager().printMessage(Kind.ERROR, "Internal error in the mapping processor: " + reportableStacktrace, element);
    }

    private void processMapperTypeElement(ModelElementProcessor.ProcessorContext context, TypeElement mapperTypeElement) {
        Object model = null;
        Iterator var4 = this.getProcessors().iterator();

        while(var4.hasNext()) {
            ModelElementProcessor<?, ?> processor = (ModelElementProcessor)var4.next();

            try {
                model = this.process(context, processor, mapperTypeElement, model);
            } catch (AnnotationProcessingException var7) {
                this.processingEnv.getMessager().printMessage(Kind.ERROR, var7.getMessage(), var7.getElement(), var7.getAnnotationMirror(), var7.getAnnotationValue());
                break;
            }
        }

    }

    private <P, R> R process(ModelElementProcessor.ProcessorContext context, ModelElementProcessor<P, R> processor, TypeElement mapperTypeElement, Object modelElement) {
        return processor.process(context, mapperTypeElement, modelElement);
    }

    private Iterable<ModelElementProcessor<?, ?>> getProcessors() {
        Iterator<ModelElementProcessor> processorIterator = ServiceLoader.load(ModelElementProcessor.class, MappingProcessor.class.getClassLoader()).iterator();
        List<ModelElementProcessor<?, ?>> processors = new ArrayList();

        while(processorIterator.hasNext()) {
            processors.add((ModelElementProcessor)processorIterator.next());
        }

        Collections.sort(processors, new ProcessorComparator());
        return processors;
    }

    private TypeElement asTypeElement(Element element) {
        return (TypeElement)element.accept(new ElementKindVisitor6<TypeElement, Void>() {
            public TypeElement visitTypeAsInterface(TypeElement e, Void p) {
                return e;
            }

            public TypeElement visitTypeAsClass(TypeElement e, Void p) {
                return e;
            }
        }, (Object)null);
    }

    private static class DeferredMapper {
        private final TypeElement deferredMapperElement;
        private final Element erroneousElement;

        private DeferredMapper(TypeElement deferredMapperElement, Element erroneousElement) {
            this.deferredMapperElement = deferredMapperElement;
            this.erroneousElement = erroneousElement;
        }
    }

    private static class ProcessorComparator implements Comparator<ModelElementProcessor<?, ?>> {
        private ProcessorComparator() {
        }

        public int compare(ModelElementProcessor<?, ?> o1, ModelElementProcessor<?, ?> o2) {
            return Integer.compare(o1.getPriority(), o2.getPriority());
        }
    }
}

手写注解处理器

  1.   自定义一个Annotation Processor,需要继承javax.annotation.processing.AbstractProcessor,并覆写process方法。
  2. 自定义一个注解,注解的元注解需要指定@Retention(RetentionPolicy.SOURCE)。
  3. 需要在声明的自定义Annotation Processor中使用javax.annotation.processing.SupportedAnnotationTypes指定在第2步创建的注解类型的名称(注意需要全类名,"包名.注解类型名称",否则会不生效)。
  4. 需要在声明的自定义Annotation Processor中使用javax.annotation.processing.SupportedSourceVersion指定编译版本。
  5. 可选操作,可以通在声明的自定义Annotation Processor中使用javax.annotation.processing.SupportedOptions指定编译参数。
  6. 通过服务注册指定,META-INF/services/javax.annotation.processing.Processor文件中添加 前面定义AnnotationProcessor

定义注解Demo

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Demo {

}

POM文件

 <dependencies>
        <!-- https://mvnrepository.com/artifact/com.google.auto.service/auto-service -->
        <dependency>
            <groupId>com.google.auto.service</groupId>
            <artifactId>auto-service</artifactId>
            <version>1.0-rc7</version>
        </dependency>

        <!-- 提供JCTree等一些功能api -->
        <dependency>
            <groupId>com.sun</groupId>
            <artifactId>tools</artifactId>
            <version>1.8</version>
            <scope>system</scope>
            <systemPath>${java.home}/../lib/tools.jar</systemPath>
        </dependency>
    </dependencies>

定义处理器AnnotationProcessor

@AutoService(Processor.class)
@SupportedAnnotationTypes(value = {"com.test.lombok.Demo"})
@SupportedSourceVersion(value = SourceVersion.RELEASE_8)
public class AnnotationProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Context context = ((JavacProcessingEnvironment) this.processingEnv).getContext();
        JavacElements elementUtils = (JavacElements) this.processingEnv.getElementUtils();
        TreeMaker treeMaker = TreeMaker.instance(context);

        JCTree.JCMethodDecl jcMethodDecl;
        Iterator iterator = roundEnv.getElementsAnnotatedWith(Demo.class).iterator();

        while (iterator.hasNext()) {
            Element element = (Element) iterator.next();
            jcMethodDecl = (JCTree.JCMethodDecl) elementUtils.getTree(element);
            treeMaker.pos = jcMethodDecl.pos;
            jcMethodDecl.body = treeMaker.Block(0L, List.of(treeMaker.Exec(treeMaker.Apply(List.nil(), treeMaker.Select(treeMaker.Select(treeMaker.Ident(elementUtils.getName("System")), elementUtils.getName("out")), elementUtils.getName("println")), List.of(treeMaker.Literal("这是HelloWorld打印的")))), jcMethodDecl.body));
        }

        return false;
    }
}

 测试类定义

public class Student {


    private Integer id;

    private String name;

    @Demo
    public static void main(String[] args) {
        System.out.println("这是Test自己打印的");
    }
}

参考文档

Java 8 中文版 - 在线API中文手册 - 码工具

AbstractProcessor (Java Platform SE 8 )

mapstruct原理解析_datastructure18的博客-CSDN博客_mapstruct原理

玩转MapStruct,手把手带你学会! - 知乎

https://mapstruct.org/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值