首页 > 其他分享 >插入式注解

插入式注解

时间:2023-04-18 23:36:14浏览次数:32  
标签:javac new 插入式 JavacProcessingEnvironment 注解 public 处理器

目录

0x01 Lombok

Lombok是Java开源的工具类库,用于对Java源码的增强,提高开发者的开发效率,例如在POJO上添加@Getter和@Setter注解,源码经过编译后会自动生成私有属性的get和set方法,在Java的日常开发中基本属于必备的工具。这种实现是Java在前端编译时,基于给定的规则,动态生成语法树节点,并插入到对应的父节点上,这就是Javac提供的插入式注解处理器的能力

深入解析Java编译器:源码剖析与实例详解 - 第8章

Java在JDK 1.5版本中提供了对注解(Annotation)的支持,在JDK 1.6版本中又提供了插入式注解处理API(Pluggable Annotation Processing API),我们可以编写自定义的注解处理器并使用这些API来完成相应的功能

...

0x02 插入式注解处理器

插入式注解处理器的执行时机是在语法分析之后语义分析之前,抽象语法树生成处理的阶段,如下:

转变过程

AbstractProcessor

自定义注解处理器需要继承AbstractProcessor类,需要覆写以下4个方法:

  • init()方法:接收ProcessingEnvironment类型的参数可以初始化许多注解操作的工具类
  • getSupportedOptions()方法:器配置支持的选项,如果不覆写这个方法,AbstractProcessor类中默认的实现会读取@SupportedOptions注解的配置;
  • getSupportedAnnotationTypes()方法:配置支持的注解类型,如果不覆写这个方法,AbstractProcessor类中默认的实现会读取@SupportedAnnotationTypes注解的配置;
  • getSupportedSourceVersion()方法:配置支持的JDK版本。如果不覆写这个方法,AbstractProcessor类中默认的实现会读取@SupportedSourceVersion注解的配置。

注解配置参数

  • -XprintProcessorInfo命令:输出有关请求处理程序处理哪些注解的信息。
  • -XprintRounds命令:输出有关注解处理循环的信息。
  • -processor命令:使用类的全限定名指定具体的注解处理器类,如chapter8.GetSet Processor,因为类要通过loadClass()方法来加载,该方法要求加载的类必须是全限定名。可以指定多个处理器,多个处理器用逗号隔开。
  • -processpath命令:指定搜索注解处理器的路径,如果没有指定此选项,默认在类路径classpath中搜索。
  • -proc:命令:当命令为-proc:none时不对注解进行任何处理,仅编译Java源文件;当命令为-proc:only时仅运行注解处理器,不需要编译Java源文件。
  • -Xprint命令:如果配置了这个命令,则会运行Javac本身提供的一个注解处理器类PrintingProcessor,这个类会打印当前正在编译的Java类的源代码。需要注意的是,指定这个命令会导致其他注解处理器类失效。
  • -Akey=value:可以为正在执行的注解处理器提供一些客户端参数,不过需要在注解处理器上预先配置,可以通过注解@SupportedOptions或者覆写方法getSupported Options()来进行配置。

在JavacProcessingEnvironment类的构造方法中读取配置命令的值,然后通过成员变量进行保存

// 来源:com.sun.tools.javac.processing.JavacProcessingEnvironment
public final boolean printProcessorInfo;
public final boolean printRounds;
public final boolean procOnly;
public final Map<String, String> processorOptions;

在JavacProcessingEnvironment类的构造方法中初始化这些成员变量,代码如下

// 来源:com.sun.tools.javac.processing.JavacProcessingEnvironment
printProcessorInfo = options.isSet(XPRINTPROCESSORINFO);
printRounds = options.isSet(XPRINTROUNDS);
procOnly = options.isSet(PROC, "only") || options.isSet(XPRINT);
processorOptions = initProcessorOptions(context);

工具类

JavacProcessingEnvironment类定义了几个工具类相关的成员变量

// 来源:com.sun.tools.javac.processing.JavacProcessingEnvironment
private final JavacFiler filer;
private final JavacMessager messager;
private final JavacElements elementUtils;
private final JavacTypes typeUtils;

在JavacProcessingEnvironment类的构造方法中初始化这些成员变量

// 来源:com.sun.tools.javac.processing.JavacProcessingEnvironment
filer = new JavacFiler(context);
messager = new JavacMessager(context, this);
elementUtils = JavacElements.instance(context);
typeUtils = JavacTypes.instance(context);

介绍以上4个工具类:

  • JavacFiler类:用来创建新的Java源文件、Class文件及辅助文件。

  • Messager类:用来报告错误、警告或其他提示信息。

  • JavacElements类:实现了javax.lang.model.util.Elements接口,用于操作Element的工具方法。

    • Symbol及相关的子类,它们都直接或间接实现了javax.lang.model.element包中定义的一些接口
  • JavacTypes类:实现了javax.lang.model.util.Types接口,用于操作TypeMirror的工具方法。

    • TypeMirror可以将Type及相关的子类型映射为TypeMirror规定的一套接口,将Symbol及相关的子类型映射为Element规定的一套接口,这样就可以在注解处理器中访问Javac内部才能使用的Symbol与Type对象

0x03初始化注解处理器

Javac在词法分析(抽象语法树)时会初始化注解处理器

JavaCompiler

//  来源:src/com/sun/tools/javac/main/JavaCompiler.java     
public void compile(List<JavaFileObject> sourceFileObjects,
                        List<String> classnames,
                        Iterable<? extends Processor> processors){
    ...
	// 初始化注解处理器
    initProcessAnnotations(processors);  
    ...
}
// 初始化注解处理器
public void initProcessAnnotations(Iterable<? extends Processor> processors) {
    ...
    // 初始化注解构造器
    procEnvImpl = new JavacProcessingEnvironment(context, processors);
    processAnnotations = procEnvImpl.atLeastOneProcessor();
    ...
}

JavacProcessingEnvironment

// 来源:src/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
// 在JavacProcessingEnvironment类的构造方法中读取配置命令的值,然后通过成员变量进行保存
public JavacProcessingEnvironment(Context context, Iterable<? extends Processor> processors) {
	...
    // 初始化参数
    printProcessorInfo = options.isSet(XPRINTPROCESSORINFO);
	...
    // processorOptions保存-Akey=value配置命令的值
    processorOptions = initProcessorOptions(context);
	...
    messages = JavacMessages.instance(context);
    // 初始化processorIterator变量
    initProcessorIterator(context, processors);
}
private void initProcessorIterator(Context context, Iterable<? extends Processor> processors) {
   		....
        // 首先获取-processor命令指定的注解处理器,
        String processorNames = options.get(PROCESSOR);
    	// 拿到文件管理器
        JavaFileManager fileManager = context.get(JavaFileManager.class);
        try {
			...
            // 当配置了-processpath命令时,会在此路径下调用fileManager.getClassLoader()方法创建对应的类加载器,
            // 否则在classpath路径下创建对应的类加载器。
            // 如果获取到的值processorNames不为空,也就是配置了-processor命令
            if (processorNames != null) {
                // 那么创建一个NameProcessIterator迭代器对象
                processorIterator = new NameProcessIterator(processorNames, processorClassLoader, log);
            } else {
                // 否则创建一个ServiceIterator迭代器对象
                processorIterator = new ServiceIterator(processorClassLoader, log);
            }
        } catch (SecurityException e) {
            ...
        }
    }
    // 初始化discoveredProcs变量
    discoveredProcs = new DiscoveredProcessors(processorIterator);
}

JavacProcessingEnvironment#DiscoveredProcessors

// 来源:src/com/sun/tools/javac/processing/JavacProcessingEnvironment#DiscoveredProcessors
// DiscoveredProcessors类实现了Iterable<ProcessorState>接口,
// 使用迭代器类ProcessorStateIterator来迭代ProcessorState对象,
// 这个迭代器也是借助processorIterator来完成注解处理器迭代的
class DiscoveredProcessors implements Iterable<ProcessorState> {
    // processorIterator保存了NameProcessIterator或ServiceIterator对象
    Iterator<? extends Processor> processorIterator;
    // procStateList保存了当前已经被封装为ProcessorState对象的所有注解处理器
    ArrayList<ProcessorState>  procStateList;
}

JavacProcessingEnvironment#ProcessorState

// 来源:src/com/sun/tools/javac/processing/JavacProcessingEnvironment#ProcessorState
static class ProcessorState {
    // 保存的具体的注解处理器
    public Processor processor;
    // contributed表示此注解处理器是否运行过process()方法
    // 如果运行过process()方法,则这个变量的值将被设置为true
    public boolean   contributed;
    // 保存了注解处理器能够处理的注解类型
    private ArrayList<Pattern> supportedAnnotationPatterns;
    // 保存了注解处理器能够处理的注解选项
    private ArrayList<String>  supportedOptionNames;
        ProcessorState(Processor p, Log log, Source source, ProcessingEnvironment env) {
        processor = p;
        contributed = false;
        try {
            // 处理注解处理器的初始化信息
            processor.init(env);
            // 处理注解处理器支持的Java源代码版本
            checkSourceVersionCompatibility(source, log);
            // 处理注解处理器支持处理的注解类型
            supportedAnnotationPatterns = new ArrayList<Pattern>();
            // 调用注解处理器的getSupportedAnnotationType()方法来获取支持处理的注解类型
            // 并添加到supportedAnnotationPatterns集合中
            for (String importString : processor.getSupportedAnnotationTypes()) {
                supportedAnnotationPatterns.add(importStringToPattern(importString,
                                                                      processor,
                                                                      log));
            }
            // 处理注解处理器支持的注解选项
            supportedOptionNames = new ArrayList<String>();
            // 调用注解处理器的getSupportedOptions()方法来获取支持的注解选项
            for (String optionName : processor.getSupportedOptions() ) {
                if (checkOptionName(optionName, log))
                    supportedOptionNames.add(optionName);
            }
        } catch (Throwable t) {
            throw new AnnotationProcessingError(t);
        }
    }
}    

以上为注解处理器初始化关键代码

最终,注解处理器被封装为JavacProcessingEnvironment#ProcessorState进行初始化

并存储在JavacProcessingEnvironment#DiscoveredProcessors的迭代器中

为后续的运行做准备

0x04运行注解处理器

JavaCompiler

//  来源:src/com/sun/tools/javac/main/JavaCompiler.java     
public void compile(List<JavaFileObject> sourceFileObjects,
                        List<String> classnames,
                        Iterable<? extends Processor> processors){
    ...
    // 调用parseFiles()方法得到List<JCCompilationUnit>对象
    // 调用enterTrees()方法完成符号的输入
    delegateCompiler = processAnnotations(
      // 不过在运行注解处理器之前还会调用enterTrees()方法,
      // 这个方法会完成符号输入的第一与第二阶段,
      // 同时也会对声明及定义的语法树节点进行标注,
      // 因此才能在后续的注解处理器运行阶段操作TypeMirror与Element
      enterTrees(stopIfError(CompileState.PARSE, parseFiles(sourceFileObjects))),
                    _); 
    ...
}
public JavaCompiler processAnnotations(List<JCCompilationUnit> roots,_) {
    ...
    // 在这之前会判断是否有必要调用JavacProcessingEnvironment类的doProcessing()方法运行注解处理器
    // 调用方法后将返回一个新的JavaCompiler对象,
    // 使用这个对象将继续执行Java源代码的编译,
    // 所以说注解处理器能够影响Javac的编译过程        
    JavaCompiler c = procEnvImpl.doProcessing(context, roots, _, _);
    ...
    return c;
}

JavacProcessingEnvironment.doProcessing

// 来源:src/com/sun/tools/javac/processing/JavacProcessingEnvironment
// 执行注解处理器处理
public JavaCompiler doProcessing(Context context,
                                 List<JCCompilationUnit> roots,
                                 List<ClassSymbol> classSymbols,
                                 Iterable<? extends PackageSymbol> pckSymbols) {
    ...
    // 出现错误退出标识
    boolean errorStatus;
    // 生成新文件标识
    boolean moreToDo;
    /*
    如果注解处理器运行process()方法后产生了新的Java源文件,
    Javac会重新运行一轮注解处理器,因此只要运行一轮注解处理器后有新的Java源文件产生后,
    就会接着重新运行一轮注解处理器,直到没有新的文件产生
     */
    do {
        // 运行第一轮的注解处理器
        // 调用Round对象的run()方法来执行注解处理的逻辑,
        // Round对象代表了循环调用注解处理器处理语法树的过程
        round.run(false, false);
        // 当运行完这一轮注解处理器时,如果没有发现错误并且又有新的文件
        // 生成时,需要进行下一轮注解处理器
        errorStatus = round.unrecoverableError();
        // 如果有新的文件产生,也就是当调用moreToDo()方法返回true时
        // 需要调用当前Round对象的next()方法得到一个新的Round对象,
        // 并将保存了新产生的文件的集合传递给新的Round对象
        moreToDo = moreToDo();
        round.showDiagnostics(errorStatus || showResolveErrors);
        // 调用round.next()方法创建新的Round对象
        // 每一次循环都会创建一个Round对象
        round = round.next(
                new LinkedHashSet<JavaFileObject>(filer.getGeneratedSourceFileObjects()),
                new LinkedHashMap<String,JavaFileObject>(filer.getGeneratedClasses()));
         ...
    } while (moreToDo && !errorStatus);
    // 运行最后一轮注解处理器
    round.run(true, errorStatus);
	...
    return compiler;
}

JavacProcessingEnvironment#Round

来源:com.sun.tools.javac.processing.JavacProcessingEnvironment.Round
// 创建一个新的Round
// 参数classSymbols列表一般为空
Round(Context context, List<JCCompilationUnit> roots, List<ClassSymbol> classSymbols) {
	...
    // 调用getTopLevelClasses()方法就是将roots列表中保存的所有编译单元下定义的顶层类追加到topLevelClasses列表中
    topLevelClasses =
        getTopLevelClasses(roots).prependList(classSymbols.reverse());
    packageInfoFiles = getPackageInfoFiles(roots);
    // 调用findAnnotationsPresent()方法查找在topLevelClasses列表的顶层类中使用到的注解类型
    findAnnotationsPresent();
}

// 调用findAnnotationsPresent()方法查找在topLevelClasses列表的顶层类中使用到的注解类型
// 查找所有使用到的注解类型并保存到Round类的annotationsPresent中
void findAnnotationsPresent() {
    // 通过ComputeAnnotationSet类对语法树进行扫描,找到使用到的注解类型
    ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils);
    annotationsPresent = new LinkedHashSet<TypeElement>();
    for (ClassSymbol classSym : topLevelClasses)
        annotationComputer.scan(classSym, annotationsPresent);
    for (PackageSymbol pkgSym : packageInfoFiles)
        annotationComputer.scan(pkgSym, annotationsPresent);
}
// 运行注解处理器
void run(boolean lastRound, boolean errorStatus) {
    ...
    try {
        // 第一轮注解处理器的调用时,lastRound为false
        // lastRound为true,标识最后一轮注解处理器
        if (lastRound) {
            filer.setLastRound(true);
            Set<Element> emptyRootElements = Collections.emptySet(); // immutable
            RoundEnvironment renv = new JavacRoundEnvironment(true,
                    errorStatus,
                    emptyRootElements,
                    JavacProcessingEnvironment.this);
            discoveredProcs.iterator().runContributingProcs(renv);
        } else {
            // 第一轮时调用
            discoverAndRunProcs(context, annotationsPresent, topLevelClasses, packageInfoFiles);
        }
    }
    ...
}

JavacProcessingEnvironment#ComputeAnnotationSet

public static class ComputeAnnotationSet extends
    ElementScanner7<Set<TypeElement>, Set<TypeElement>> {
    final Elements elements;
    public ComputeAnnotationSet(Elements elements) {
        super();
        this.elements = elements;
    }
    @Override
    public Set<TypeElement> visitPackage(PackageElement e, Set<TypeElement> p) {
        // Don't scan enclosed elements of a package
        return p;
    }
    @Override
    // 在这个方法中查找所有已被使用的注解类型
    public Set<TypeElement> scan(Element e, Set<TypeElement> p) {
        // 如果找到,就将注解类型对应的Element对象存到p集合中,
        // 也就是保存到了Round类中定义的类型为Set<TypeElement>的annotationsPresent集合。
        for (AnnotationMirror annotationMirror :
                 elements.getAllAnnotationMirrors(e) ) {
            Element e2 = annotationMirror.getAnnotationType().asElement();
            // 将需要处理的注解类型放入Set中
            p.add((TypeElement) e2);
        }
        return super.scan(e, p);
    }
}

JavacProcessingEnvironment#DiscoveredProcessors

// 来源:src/com/sun/tools/javac/processing/JavacProcessingEnvironment#DiscoveredProcessors
// 运行注解处理器
private void discoverAndRunProcs(Context context,
                                 Set<TypeElement> annotationsPresent,
                                 List<ClassSymbol> topLevelClasses,
                                 List<PackageSymbol> packageInfoFiles) {
    // 需要处理的元素的全限定名和元素的映射
    Map<String, TypeElement> unmatchedAnnotations =
        new HashMap<String, TypeElement>(annotationsPresent.size());
    // 建立全限定名到对应TypeElement对象的映射关系
    for(TypeElement a  : annotationsPresent) {
            unmatchedAnnotations.put(a.getQualifiedName().toString(),
                                     a);
    }
    // 让处理"*"的注解处理器也有机会运行
    if (unmatchedAnnotations.size() == 0)
        unmatchedAnnotations.put("", null);
    // 可通过迭代器获取所有的注解处理器
    DiscoveredProcessors.ProcessorStateIterator psi = discoveredProcs.iterator();
    Set<Element> rootElements = new LinkedHashSet<Element>();
    rootElements.addAll(topLevelClasses);
    rootElements.addAll(packageInfoFiles);
    rootElements = Collections.unmodifiableSet(rootElements);
    // 准备这一轮Round运行的环境
    // renv就是在调用注解处理器的process()方法时传递的第2个RoundEnvironment类型的参数
    // renv会保存上一轮Round运行后的一些状态,可以在覆写process()方法时调用相关方法获取这些信息进行逻辑处理
    RoundEnvironment renv = new JavacRoundEnvironment(false,
                                                      false,
                                                      rootElements,
                                                      JavacProcessingEnvironment.this);
    // 当有待处理的注解并且有注解处理器的情况下,查找能处理注解的注解处理器并运行
    while(unmatchedAnnotations.size() > 0 && psi.hasNext() ) {
        // 调用psi.next()方法获取ProcessorState对象
        ProcessorState ps = psi.next();
        // 匹配出可以被处理的注解名
        Set<String>  matchedNames = new HashSet<String>();
        // 匹配出可以被处理的元素
        Set<TypeElement> typeElements = new LinkedHashSet<TypeElement>();
        // 查找注解处理器能够处理的注解并存储到matchedNames集合中
        // 当unmatchedAnnotations集合中存在注解类型并且也能查找到注解处理器时,查找能处理这些注解类型的注解处理器并运行
        for (Map.Entry<String, TypeElement> entry: unmatchedAnnotations.entrySet()) {
            String unmatchedAnnotationName = entry.getKey();
            // 从unmatchedAnnotations集合中查找是否含有能被当前的注解处理器ps处理的注解类型
            if (ps.annotationSupported(unmatchedAnnotationName) ) {
                // 添加匹配出可以被处理的注解
                matchedNames.add(unmatchedAnnotationName);
                TypeElement te = entry.getValue();
                if (te != null)
                    // 添加匹配出可以被处理的元素
                    // TypeElement对象就是注解处理器覆写process()方法时接收的
                    // 第一个Set<? extends TypeElement>类型的参数,表示此注解处理器处理的注解类型
                    typeElements.add(te);
            }
        }
        // 当注解处理器ps能够处理某些注解或者在之前的Round中运行过此注解处理器时
        if (matchedNames.size() > 0 || ps.contributed) {
            // 调用callProcessor()方法运行此注解处理器
            boolean processingResult = callProcessor(ps.processor, typeElements, renv);
            ps.contributed = true;
            ps.removeSupportedOptions(unmatchedProcessorOptions);
            if (printProcessorInfo || verbose) {
                log.printNoteLines("x.print.processor.info",
                        ps.processor.getClass().getName(),
                        matchedNames.toString(),
                        processingResult);
            }
            // 注解处理器执行成功,移除处理器名称
            if (processingResult) {
                unmatchedAnnotations.keySet().removeAll(matchedNames);
            }
        }
    }
    unmatchedAnnotations.remove("");
	...
    // 再次运行之前Round中运行过的注解处理器
    psi.runContributingProcs(renv);
	...
}
// 运行在procStateList中剩下的还没有运行过的注解处理器
public void runContributingProcs(RoundEnvironment re) {
    if (!onProcInterator) {
        Set<TypeElement> emptyTypeElements = Collections.emptySet();
        while(innerIter.hasNext()) {
            ProcessorState ps = innerIter.next();
            if (ps.contributed)
                callProcessor(ps.processor, emptyTypeElements, re);
        }
    }
}

以上为运行注解处理器的关键代码,大致流程为:

  1. JavaCompiler.processAnnotations()处理注解
  2. JavacProcessingEnvironment.doProcessing()执行处理
    1. JavacProcessingEnvironment#Round多轮调用执行,因为存在编译过程中生成新的文件
      1. JavacProcessingEnvironment#ComputeAnnotationSet查找所有被处理注解打标的文件
      2. JavacProcessingEnvironment#Round.run()执行注解处理器处理
        1. DiscoveredProcessors.discoverAndRunProcs()运行注解处理器
        2. DiscoveredProcessors.runContributingProcs()运行在procStateList中剩下的还没有运行过的注解处理器

0x05 自己实现@getter

自定义注解处理器类

package book.chapter8;

import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.*;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;

/**
 * 自定义get处理器
 */
@SupportedAnnotationTypes({ "book.chapter8.Getter" })// 注解类全路径
@SupportedSourceVersion(SourceVersion.RELEASE_7)     // 支持的jdk版本
public class GetSetProcessor extends AbstractProcessor {
  // 用来报告错误、警告或其他提示信息
  private Messager messager;
  // java语法树上下文
  private JavacTrees trees;
  // Java语法树构造器
  private TreeMaker treeMaker;
  // 可以用来创建新的符号
  private Names names;
  
  // 初始化,准备环境和工具类
  @Override
  public synchronized void init(ProcessingEnvironment pe) {
    super.init(pe);
    messager = pe.getMessager();
    // 初始化一颗树
    this.trees = JavacTrees.instance(processingEnv);
    // 拿到当前环境的上下文
    Context context = ((JavacProcessingEnvironment) pe).getContext();
    // 初始化一个语法树构造器
    this.treeMaker = TreeMaker.instance(context);
    // 初始化一个符号构造
    this.names = Names.instance(context);
  }
    
  // 处理
  @Override
  public boolean process(Set<? extends TypeElement> set, RoundEnvironment re) {
    // 获取getter注解关联的类
    Set<? extends Element> elems = re.getElementsAnnotatedWith(Getter.class);
    // new一个树翻译器
    TreeTranslator tt = new TreeTranslator() {
      @Override
      public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
        List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
        // 将类中定义的成员变量保存到jcVariableDeclList列表中
        for (JCTree tree : jcClassDecl.defs) {
          if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
            JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
            jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
          }
        }
        // 处理每一个成员变量
        for (JCTree.JCVariableDecl jcVariableDecl : jcVariableDeclList) {
          messager.printMessage(Diagnostic.Kind.NOTE, jcVariableDecl.getName() + " has been processed");
          // 调用makeGetterMethodDecl()方法为成员变量创建getXxx()方法
          // 对应的语法树并追加到jcClassDecl.defs中
          jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
        }
        super.visitClassDef(jcClassDecl);
      }
    };
    // 将上面的翻译器放到每个元素中
    for (Element element : elems) {
      JCTree jcTree = trees.getTree(element);
      jcTree.accept(tt);
    }
    return true;
  }
  // 为成员变量创建getXxx()方法对应的语法树
  private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
    // 描述列表
    ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
    // this关键字描述
    JCTree.JCIdent jci = treeMaker.Ident(names.fromString("this"));
    // 变量描述,放入this关键字
    JCTree.JCFieldAccess jcf = treeMaker.Select(jci, jcVariableDecl.getName());
    // return 描述,放入变量描述
    JCTree.JCReturn jcr = treeMaker.Return(jcf);
    // 放入到描述列表
    statements.append(jcr);
    // 方法体位空
    JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
    // 方法定义
    return treeMaker.MethodDef(
        // 访问等级
        treeMaker.Modifiers(Flags.PUBLIC),
        // 方法名
        getNewMethodName(jcVariableDecl.getName()),
        // 返回值类型
        jcVariableDecl.vartype,
        // 参数类型列表为空
        List.<JCTree.JCTypeParameter>nil(),
        // 参数描述列表为空
        List.<JCTree.JCVariableDecl>nil(),
        // 参数表达式为空(比如...)
        List.<JCTree.JCExpression>nil(),
        // 方法体
        body,
        null);
  }
  // 获取getXxx的方法名
  private Name getNewMethodName(Name name) {
    String s = name.toString();
    String mn = "get" + s.substring(0, 1).toUpperCase() + s.substring(1,name.length());
    return names.fromString(mn);
  }
}

自定义的注解类

package book.chapter8;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter { }

写一个类测试

package book.chapter8;
@Getter
public class TestAnnotation {
  private String name;
  private int age;
}

测试

package book.chapter8;

import javax.tools.ToolProvider;

public class Test{
  public static void main(String[] args) {
    javax.tools.JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
    int results = compiler.run(null, null, null, new String[]{
            // -processor命令指定具体的注解处理器类
            "-processor","book.chapter8.GetSetProcessor",
            // -processorpath命令指定搜索注解处理器的路径
            "-processorpath","/Users/yangluchao/Documents/GitHub/javac_study/src/book/chapter8",
            // 运行后会根据/Users/yangluchao/Documents/GitHub/javac_study/src/book/chapter8/TestAnnotation.java
            // 在“/Users/yangluchao/Documents/GitHub/javac_study/save/ylcComplieTest”路径下
            // 生成TestAnnotation.class类
            "-d","/Users/yangluchao/Documents/GitHub/javac_study/save/ylcComplieTest",
            "/Users/yangluchao/Documents/GitHub/javac_study/src/book/chapter8/TestAnnotation.java",
    });
  }
}

执行结果

image

0x06 总结

有了插入式注解处理器让Java的开发更加灵活,在性能监控、安全扫描、业务插桩等方面可以有比较深入的应用。在日常开发中也更加便利(比如lombok)

标签:javac,new,插入式,JavacProcessingEnvironment,注解,public,处理器
From: https://www.cnblogs.com/ylc0x01/p/17331634.html

相关文章

  • pringboot之restfull接口规范注解(二)
    1,springboot逆向mybatis生成接口类2,执行generator生成接口类1,控制台使用mvn命令:2,双击mvn里面的pulgins插件下的renerator启动插件3,创建一个控制器4,application配置文件添加引入mapper的xml路径5,执行效果6,测试代码地址本文永久更新地址:1,springboot逆向mybatis......
  • Java 4种校验注解(值校验、范围校验、长度校验、格式校验)
    1Maven依赖<!--第一种方式导入校验依赖--><dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId><version>2.0.1.Final</version></dependency><!--第二种方式导入校验......
  • Spring AOP官方文档学习笔记(二)之基于注解的Spring AOP
    1.@Aspect注解(1)@Aspect注解用于声明一个切面类,我们可在该类中来自定义切面,早在Spring之前,AspectJ框架中就已经存在了这么一个注解,而Spring为了提供统一的注解风格,因此采用了和AspectJ框架相同的注解方式,这便是@Aspect注解的由来,换句话说,在Spring想做AOP框架之前,AspectJAOP框......
  • SpingROOT注解
    @TableName("CG_WO_FAULT_EXPERT_PAPERS")实体类加这个注解 controller直接可以去查询表了@Accessors(chain=true)注解是用来干嘛的?这个注解是来自与Lombok里的,具体的作用是开启链式编程,让我们写代码更加方便。.排查 经过排查发现是因为 @ApiModel 直接使用不规范导......
  • 常用注解
    1.注解注册bean:将某个类装配到spring容器中进行托管,以下四个注解功能一样,使用的地方不同@Component:通用组件@Repository:一般在dao层使用@Controller:一般在controller层使用@Service:一般在service层使用使用bean:@Autowired:该注解可以自动对类的成员变量、构造函数进行......
  • 05.单元测试、注解和反射
    1、单元测试什么是单元测试?单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法,因此,单元测试就是针对Java方法的测试,进而检查方法的正确性。目前测试方法是怎么进行的,存在什么问题?只有一个main方法,如果一个方法的测试失败了,其他方法测试会受到影响......
  • @Valid注解
    使用@Valid注解如:publicRespBeandoLogin(@ValidLoginVologinVo,HttpServletRequestrequest,HttpServletResponseresponse){log.info("{}",loginVo);returntUserService.doLongin(loginVo,request,response);}LoginVo@Datapubl......
  • @RestControllerAdvice注解 @ExceptionHandler注解
    RestControllerAdvice+ExceptionHandler这两个注解的组合,被用作项目的全局异常处理。一旦项目中发生了异常,就会进入使用了RestControllerAdvice注解类中使用了ExceptionHandler注解的方法。下面是一些项目全局异常的处理@ControllerAdvice(annotations={RestController.class,......
  • Q:数据库方法的传播特性,外层方法的事务注解@Transactional默认会影响本方法么
    外层方法的事务注解默认会影响本方法么涉及知识:事务的传播特性实验前推测:目前了解内、外方法某个发生异常执行回滚是否影响另一个方法是由配置的哪个传播特性决定的。推测内方法出现异常要导致外方法的事务也要回滚,因为这个在现实场景最普遍。实验:描述:roleService.inse......
  • SpringBoot常用注解
    本文整理了SpringBoot常用注解,主要讲解这些注解的用法,并附上一致思维导图。SpringBoot常用注解组件相关注解@Controller用于修饰MVC中controller层的组件,SpringBoot中的组件扫描功能会识别到该注解,并为修饰的类实例化对象,通常与@RequestMapping联用,当SpringMVC获取到请求时......