Java JSR-269 插入式注解处理器

文章目录

    • JSR-269 & 什么是插入式注解处理器
    • 1、快速开始
    • 2、语法树相关简介
      • 2.1、JCTree
      • 2.2、TreeMaker
      • 2.2.1、TreeMaker.Modifiers
      • 2.2.2、TreeMaker.ClassDef
      • 2.2.3、TreeMaker.MethodDef
      • 2.2.4、TreeMaker.VarDef
      • 2.2.5、TreeMaker.Ident
      • 2.2.6、TreeMake.Return
      • 2.2.7、TreeMaker.Select
      • 2.2.8、TreeMaker.NewClass
      • 2.2.9、TreeMaker.Apply
      • 2.2.10、TreeMaker.Assign
      • 2.2.11、TreeMaker.Exec
      • 2.2.12、TreeMaker.Block
      • 2.3、com.sun.tools.javac.util.List
      • 2.4、com.sun.tools.javac.util.ListBuffer
      • 3.5、 tricky
      • 3.5.1、创建一个构造方法
      • 3.5.2、创建一个方法的参数
      • 3.5.3、创建一条赋值语句
      • 3.5.4、创建一条new语句
      • 3.5.5、创建一条方法调用语句
      • 3.3.6、从JCTree.JCVariable中获取类型信息
    • 注意事项

JSR-269 & 什么是插入式注解处理器

在Javac源码中,插入式注解处理器的初始化过程是在initPorcessAnnotations() 方法中完成的,而它的执行过程则是在processAnnotations()方法中完成的,这个方法判断是否还有新的注解处理器需要执行,如果有的话,通过com.sun.tools.javac.processing.JavacProcessingEnvironment类的 **doProcessing()**方法生成一个新的JavaCompiler对象对编译的后续步骤进行处理。

在JDK1.5之后,Java语言提供了对注解(Annotation)的支持,这些注解与普通的Java代码一样,是在运行期间发挥作用的。在**JDK1.6中实现了JSR-269规范,JSR-269:Pluggable Annotations Processing API(插入式注解处理API)。提供了一组插入式注解处理器的标准API在编译期间对注解进行处理。**我们可以把它看做是一组编译器的插件,在这些插件里面,可以读取、修改、添加抽象语法树中的任意元素。如果这些插件在处理注解期间对语法树进行了修改,编译器将回到解析及填充符号表的过程重新处理,直到所有插入式注解处理器都没有再对语法树进行修改为止,每一次循环称为一个Round,这是一个回环过程。

1、快速开始

需求,模拟lombok生成get/set方法。

实现步骤:

  • 编写get/set处理器,该类需要继承AbstractProcessor

  • 编写支持版本,使用注解**@SupportSourceVersion** 或重新方法getSupportedSourceVersion

  • 处理支持的注解,使用注解**@SupportedAnnotationTypes** 或重写方法getSupportedAnnotationTypes

  • 重写init方法

  • 重写process方法

  • 在根目录下创建文件META-INF/services/javax.annotation.processing.Processor可在resources目录下创建,其内容如下

    xin.spring.java.processor.GetterProcessor
    xin.spring.java.processor.SetterProcessor
    

在此基础上需要提供两个注解:

package xin.spring.java.processor;

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 xin.spring.java.processor;

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 Setter {

}

get编码实现:

package xin.spring.java.processor;

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;

//对Getter感兴趣
@SupportedAnnotationTypes("xin.spring.java.processor.Getter")
//支持的版本,使用1.8就写这个
//@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class GetterProcessor extends AbstractProcessor {

    // 编译时期输入日志的
    private Messager messager;

    // 将Element转换为JCTree的工具,提供了待处理的抽象语法树
    private JavacTrees trees;

    // 封装了创建AST节点的一些方法
    private TreeMaker treeMaker;

    // 提供了创建标识符的方法
    private Names names;

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

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.messager = processingEnv.getMessager();
        this.trees = JavacTrees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

        // 获取被@Getter注解标记的所有元素(这个元素可能是类、变量、方法等等)
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Getter.class);

        set.forEach(element -> {
            // 将Element转换为JCTree
            JCTree jcTree = trees.getTree(element);
            jcTree.accept(new TreeTranslator() {
                /***
                 * JCTree.Visitor有很多方法,我们可以通过重写对应的方法,(从该方法的形参中)来获取到我们想要的信息:
                 * 如: 重写visitClassDef方法, 获取到类的信息;
                 *     重写visitMethodDef方法, 获取到方法的信息;
                 *     重写visitVarDef方法, 获取到变量的信息;
                 * @param jcClassDecl
                 */
                @Override
                public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {

                    //创建一个变量语法树节点的List
                    List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();

                    // 遍历defs,即是类定义的详细语句,包括字段、方法的定义等等
                    for (JCTree tree : jcClassDecl.defs) {
                        if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
                            JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
                            jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
                        }
                    }
                    // 对于变量进行生成方法的操作
                    jcVariableDeclList.forEach(jcVariableDecl -> {
                        messager.printMessage(Diagnostic.Kind.NOTE, "get " + jcVariableDecl.getName() + " has been processed");
                        treeMaker.pos = jcVariableDecl.pos;
                        //类里的前面追加生成的Getter方法
                        jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
                    });
                    super.visitClassDef(jcClassDecl);
                }

            });
        });
        //我们有修改过AST,所以返回true
        return true;
    }

    private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
        /***
         * JCStatement:声明语法树节点,常见的子类如下
         * JCBlock:语句块语法树节点
         * JCReturn:return语句语法树节点
         * JCClassDecl:类定义语法树节点
         * JCVariableDecl:字段/变量定义语法树节点
         * JCMethodDecl:方法定义语法树节点
         * JCModifiers:访问标志语法树节点
         * JCExpression:表达式语法树节点,常见的子类如下
         * JCAssign:赋值语句语法树节点
         * JCIdent:标识符语法树节点,可以是变量,类型,关键字等等
         */
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>(); statements.append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName())));

        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());

        return treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC),//mods:访问标志
                getNewMethodName(jcVariableDecl.getName()),//name:方法名
                jcVariableDecl.vartype,//restype:返回类型
                List.nil(),//typarams:泛型参数列表
                List.nil(),//params:参数列表
                List.nil(),//thrown:异常声明列表
                body,//方法体
                null);
    }

    private Name getNewMethodName(Name name) {
        String s = name.toString();
        return names.fromString("get" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));
    }
}

set编码实现:

package xin.spring.java.processor;

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.code.Type;
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;

@SupportedAnnotationTypes("xin.spring.java.processor.Setter")
//@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class SetterProcessor extends AbstractProcessor {

    private Messager messager;

    private JavacTrees trees;

    private TreeMaker treeMaker;

    private Names names;

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

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.messager = processingEnv.getMessager();
        this.trees = JavacTrees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Setter.class);

        set.forEach(element -> {
            JCTree jcTree = trees.getTree(element);
            jcTree.accept(new TreeTranslator() {
                @Override
                public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                    List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
                    for (JCTree tree : jcClassDecl.defs) {
                        if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
                            JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
                            jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
                        }
                    }
                    jcVariableDeclList.forEach(jcVariableDecl -> {
                        messager.printMessage(Diagnostic.Kind.NOTE, "set " + jcVariableDecl.getName() + " has been processed");
                        treeMaker.pos = jcVariableDecl.pos;
                        jcClassDecl.defs = jcClassDecl.defs.prepend(makeSetterMethodDecl(jcVariableDecl));

                    });
                    super.visitClassDef(jcClassDecl);
                }

            });
        });
        return true;
    }


    private JCTree.JCMethodDecl makeSetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        statements.append(
                treeMaker.Exec(
                        treeMaker.Assign(
                                treeMaker.Select(
                                        treeMaker.Ident(names.fromString("this")),
                                        names.fromString(jcVariableDecl.name.toString())
                                ),
                                treeMaker.Ident(names.fromString(jcVariableDecl.name.toString()))
                        )
                )
        );

        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());

        // 生成入参
        JCTree.JCVariableDecl param = treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER), jcVariableDecl.getName(),jcVariableDecl.vartype, null);

        List<JCTree.JCVariableDecl> paramList = List.of(param);

        return treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC), // 方法限定值
                setNewMethodName(jcVariableDecl.getName()), // 方法名
                treeMaker.Type(new Type.JCVoidType()), // 返回类型
                List.nil(),
                paramList, // 入参
                List.nil(),
                body,
                null
        );

    }

    private Name setNewMethodName(Name name) {
        String s = name.toString();
        return names.fromString("set" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));
    }
}

如上我们就编写好了一个插入式处理器,此插入式处理器用于生成get/set方法

2、语法树相关简介

2.1、JCTree

JCTree是语法树元素的基类,包含一个重要的字段pos,该字段用于指明当前语法树节点(JCTree)在语法树中的位置,因此我们不能直接用new关键字来创建语法树节点,及时创建了也没有意义,此外,结合访问者模式,将数据结构与数据的处理进行解耦,部分源码如下:

public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
    public int pos = -1;
    ...
    public abstract void accept(JCTree.Visitor visitor);
    ...
}

JCTree的子类:

1、JCStatement:声明语法树节点,常见的子类如下:

  • JCBlock:语句块语法树节点
  • JCReturn:return语句语法树节点
  • JCClassDecl:类定义语法树节点
  • JCVariableDecl:字段/变量定义语法树节点

2、JCMethodDecl:方法定义语法树节点

3、JCModifiers:访问标志语法树节点

4、JCExpression:表达式语法树节点,常见子类如下:

  • JCAssign:赋值语句语法树节点
  • JCIdent:标识符语法树节点,可以是变量,类型,关键字等等

2.2、TreeMaker

TreeMaker用于创建一系列语法树节点,创建时会为创建出来的JCTree设置pos字段,所以必须用上下文相关的TreeMaker对象来创建语法树节点,而不能直接new语法树节点

源码可以参考:TreeMaker DOC

2.2.1、TreeMaker.Modifiers

TreeMaker.Modifiers方法用于创建访问标志语法树节点(JCModifiers),源码如下:

  • flags:访问标志
  • annotations:注解列表
public JCModifiers Modifiers(long flags) {
    return Modifiers(flags, List.< JCAnnotation >nil());
}

public JCModifiers Modifiers(long flags,
    List<JCAnnotation> annotations) {
        JCModifiers tree = new JCModifiers(flags, annotations);
        boolean noFlags = (flags & (Flags.ModifierFlags | Flags.ANNOTATION)) == 0;
        tree.pos = (noFlags && annotations.isEmpty()) ? Position.NOPOS : pos;
        return tree;
}

其中入参flags可以用枚举类型com.sun.tools.javac.code.Flags,且支持拼接(枚举值经过精心设计)

例子:treeMaker.Modifiers(Flags.PUBLIC + Flags.STATIC + Flags.FINAL);

2.2.2、TreeMaker.ClassDef

TreeMaker.ClassDef用于创建类定义语法树节点(JCClassDecl),源码如下:

  • mods:访问标志
  • name:类名
  • typarams:泛型参数列表
  • extending:父类
  • implementing:接口列表
  • defs:类定义的详细语句,包括字段,方法定义等等
public JCClassDecl ClassDef(JCModifiers mods,
    Name name,
    List<JCTypeParameter> typarams,
    JCExpression extending,
    List<JCExpression> implementing,
    List<JCTree> defs) {
        JCClassDecl tree = new JCClassDecl(mods,
                                     name,
                                     typarams,
                                     extending,
                                     implementing,
                                     defs,
                                     null);
        tree.pos = pos;
        return tree;
}

2.2.3、TreeMaker.MethodDef

TreeMake.MethodDef用于创建语法定义语法树节点(JCMethodDecl),源码如下:

  • mods:访问标志
  • name:方法名
  • restype:返回类型
  • typarams:参数列表
  • thrown:异常声明列表
  • body:方法体
  • defaultValue:默认方法(可能是interface中的那个default)
  • m:方法符号
  • mtype:方法类型。包含多种类型,泛型参数类型、方法参数类型、异常参数类型、返回参数类型
public JCMethodDecl MethodDef(JCModifiers mods,
    Name name,
    JCExpression restype,
    List<JCTypeParameter> typarams,
    List<JCVariableDecl> params,
    List<JCExpression> thrown,
    JCBlock body,
    JCExpression defaultValue) {
        JCMethodDecl tree = new JCMethodDecl(mods,
                                       name,
                                       restype,
                                       typarams,
                                       params,
                                       thrown,
                                       body,
                                       defaultValue,
                                       null);
        tree.pos = pos;
        return tree;
}

public JCMethodDecl MethodDef(MethodSymbol m,
    Type mtype,
    JCBlock body) {
        return (JCMethodDecl)
            new JCMethodDecl(
                Modifiers(m.flags(), Annotations(m.getAnnotationMirrors())),
                m.name,
                Type(mtype.getReturnType()),
                TypeParams(mtype.getTypeArguments()),
                Params(mtype.getParameterTypes(), m),
                Types(mtype.getThrownTypes()),
                body,
                null,
                m).setPos(pos).setType(mtype);
}

其中,返回类型填null或者treeMaker.TypeIdent(TypeTag.VOID)都代表返回void类型

2.2.4、TreeMaker.VarDef

TreeMaker.VarDef用于创建字段/变量定义语法树节点(JCVariableDecl),源码如下:

  • mods:访问标志
  • vartype:类型
  • init:初始化语句
  • v:变量符号
public JCVariableDecl VarDef(JCModifiers mods,
    Name name,
    JCExpression vartype,
    JCExpression init) {
        JCVariableDecl tree = new JCVariableDecl(mods, name, vartype, init, null);
        tree.pos = pos;
        return tree;
}

public JCVariableDecl VarDef(VarSymbol v,
    JCExpression init) {
        return (JCVariableDecl)
            new JCVariableDecl(
                Modifiers(v.flags(), Annotations(v.getAnnotationMirrors())),
                v.name,
                Type(v.type),
                init,
                v).setPos(pos).setType(v.type);
}

2.2.5、TreeMaker.Ident

TreeMaker.Ident用于创建标识符语法树节点(JCIdent),源码如下:

public JCIdent Ident(Name name) {
        JCIdent tree = new JCIdent(name, null);
        tree.pos = pos;
        return tree;
}

public JCIdent Ident(Symbol sym) {
        return (JCIdent)new JCIdent((sym.name != names.empty)
                                ? sym.name
                                : sym.flatName(), sym)
            .setPos(pos)
            .setType(sym.type);
}

public JCExpression Ident(JCVariableDecl param) {
        return Ident(param.sym);
}

2.2.6、TreeMake.Return

TreeMaker.Return用于创建return语句语法树节点(JCReturn),源码如下:

public JCReturn Return(JCExpression expr) {
        JCReturn tree = new JCReturn(expr);
        tree.pos = pos;
        return tree;
}

2.2.7、TreeMaker.Select

TreeMaker.Select用于创建域访问/方法访问(这里的方法访问只是取到名字,方法的调用需要调用TreeMaker.Apply)语法树节点(JCFieldAccess),源码如下:

  • selected: **.**运算符左边的表达式
  • selector: **.**运算符右边的名字
public JCFieldAccess Select(JCExpression selected,
    Name selector) 
{
        JCFieldAccess tree = new JCFieldAccess(selected, selector, null);
        tree.pos = pos;
        return tree;
}

public JCExpression Select(JCExpression base,
    Symbol sym) {
        return new JCFieldAccess(base, sym.name, sym).setPos(pos).setType(sym.type);
}

2.2.8、TreeMaker.NewClass

TreeMaker.NewClass用于创建new语句语法树节点(JCNewClass),源码如下:

  • encl:不太明白此参数含义
  • typeargs:参数类型列表
  • clazz:待创建对象的类型
  • args:参数列表
  • def:类定义
public JCNewClass NewClass(JCExpression encl,
    List<JCExpression> typeargs,
    JCExpression clazz,
    List<JCExpression> args,
    JCClassDecl def) {
        JCNewClass tree = new JCNewClass(encl, typeargs, clazz, args, def);
        tree.pos = pos;
        return tree;
}

2.2.9、TreeMaker.Apply

TreeMaker.Apply用于创建方法调用语法树节点(JCMethodInvocation),源码如下:

  • typeargs:参数类型列表
  • fn:调用语句
  • args:参数列表
public JCMethodInvocation Apply(List<JCExpression> typeargs,
    JCExpression fn,
    List<JCExpression> args) {
        JCMethodInvocation tree = new JCMethodInvocation(typeargs, fn, args);
        tree.pos = pos;
        return tree;
}

2.2.10、TreeMaker.Assign

TreeMaker.Assign用于创建赋值语句语法树节点(JCAssign),源码如下:

  • lhs:赋值语句左表达式
  • rhs:赋值语句右表达式
public JCAssign Assign(JCExpression lhs,
    JCExpression rhs) {
        JCAssign tree = new JCAssign(lhs, rhs);
        tree.pos = pos;
        return tree;
}

2.2.11、TreeMaker.Exec

TreeMaker.Exec用于创建可执行语句语法树节点(JCExpressionStatement),源码如下:

public JCExpressionStatement Exec(JCExpression expr) {
        JCExpressionStatement tree = new JCExpressionStatement(expr);
        tree.pos = pos;
        return tree;
}

例如,TreeMaker.Apply以及TreeMaker.Assign就需要外面包一层TreeMaker.Exec来获得一个JCExpressionStatement

2.2.12、TreeMaker.Block

TreeMaker.Block用于创建组合语句语法树节点(JCBlock),源码如下:

  • flags:访问标志
  • stats:语句列表
public JCBlock Block(long flags,
    List<JCStatement> stats) {
        JCBlock tree = new JCBlock(flags, stats);
        tree.pos = pos;
        return tree;
}

2.3、com.sun.tools.javac.util.List

上述JSR-269API中会涉及到一个List,这个List不是java.util.List,它是com.sun.tools.javac.util.List,这个List的操作比较奇特,不支持链式操作。下面给出部分源码,List包含两个字段,head和tail,其中head只是一个节点,而tail是一个List


public class List<A> extends AbstractCollection<A> implements java.util.List<A> {
    public A head;
    public List<A> tail;
    private static final List<?> EMPTY_LIST = new List<Object>((Object)null, (List)null) {
        public List<Object> setTail(List<Object> var1) {
            throw new UnsupportedOperationException();
        }

        public boolean isEmpty() {
            return true;
        }
    };

    List(A head, List<A> tail) {
        this.tail = tail;
        this.head = head;
    }

    public static <A> List<A> nil() {
        return EMPTY_LIST;
    }

    public List<A> prepend(A var1) {
        return new List(var1, this);
    }

    public List<A> append(A var1) {
        return of(var1).prependList(this);
    }

    public static <A> List<A> of(A var0) {
        return new List(var0, nil());
    }

    public static <A> List<A> of(A var0, A var1) {
        return new List(var0, of(var1));
    }

    public static <A> List<A> of(A var0, A var1, A var2) {
        return new List(var0, of(var1, var2));
    }

    public static <A> List<A> of(A var0, A var1, A var2, A... var3) {
        return new List(var0, new List(var1, new List(var2, from(var3))));
    }

    ...
}

2.4、com.sun.tools.javac.util.ListBuffer

由于com.sun.tools.javac.util.ListBuffer用起来不是很方便,而ListBuffer的行为与java.util.List的行为类似,并且提供了转换成com.sun.tools.javac.util.List的方法

ListBuffer<JCTree.JCStatement> jcStatements = new ListBuffer<>();

//添加语句 " this.xxx = xxx; "
jcStatements.append(...);

//添加Builder模式中的返回语句 " return this; "
jcStatements.append(...);

List<JCTree.JCStatement> lst = jcStatements.toList();

3.5、 tricky

3.5.1、创建一个构造方法

注意点:方法的名字就是

treeMaker.MethodDef(
        treeMaker.Modifiers(Flags.PUBLIC), //访问标志
        names.fromString(""), //名字
        treeMaker.TypeIdent(TypeTag.VOID), //返回类型
        List.nil(), //泛型形参列表
        List.nil(), //参数列表
        List.nil(), //异常列表
        jcBlock, //方法体
        null //默认方法(可能是interface中的那个default)
);

3.5.2、创建一个方法的参数

注意点:访问标志设置成:Flags.PARAMETER

treeMaker.VarDef(
        treeMaker.Modifiers(Flags.PARAMETER), //访问标志。极其坑爹!!!
        prototypeJCVariable.name, //名字
        prototypeJCVariable.vartype, //类型
        null //初始化语句
);

3.5.3、创建一条赋值语句

treeMaker.Exec(
        treeMaker.Assign(
                treeMaker.Select(
                        treeMaker.Ident(names.fromString(THIS)),
                        jcVariable.name
                ),
                treeMaker.Ident(jcVariable.name)
        )
)

3.5.4、创建一条new语句

treeMaker.NewClass(
        null, //尚不清楚含义
        List.nil(), //泛型参数列表
        treeMaker.Ident(builderClassName), //创建的类名
        List.nil(), //参数列表
        null //类定义,估计是用于创建匿名内部类
)

3.5.5、创建一条方法调用语句

treeMaker.Exec(
        treeMaker.Apply(
                List.nil(),
                treeMaker.Select(
                        treeMaker.Ident(getNameFromString(IDENTIFIER_DATA)),
                        jcMethodDecl.getName()
                ),
                List.of(treeMaker.Ident(jcVariableDecl.getName())) //传入的参数集合
        )
)

3.3.6、从JCTree.JCVariable中获取类型信息

注意,直接拿vartype字段,而不是type字段或者**getType()**方法

  • vartype的类型是JCTree.JCExpression
  • type的类型是com.sun.tools.javac.code.Type,这是个非标准api,理应不该使用

注意事项

【tip】:如果是maven项目需要在pom文件中配置如下内容:

目的:避免注解处理器编译出错问题


  <build>
    
    <resources>
      <resource>
        <directory>src/main/resourcesdirectory>
        <excludes>
          <exclude>META-INF/**/*exclude>
        excludes>
      resource>
    resources>

    <plugins>
      <plugin>
        <groupId>org.apache.maven.pluginsgroupId>
        <artifactId>maven-compiler-pluginartifactId>
        <version>3.1version>
        <configuration>
          <source>${maven.compiler.target}source>
          <target>${maven.compiler.target}target>
        configuration>
      plugin>
      
      <plugin>
        <groupId>org.apache.maven.pluginsgroupId>
        <artifactId>maven-resources-pluginartifactId>
        <version>2.6version>
        <executions>
          <execution>
            <id>process-METAid>
            <phase>prepare-packagephase>
            <goals>
              <goal>copy-resourcesgoal>
            goals>
            <configuration>
              <outputDirectory>target/classesoutputDirectory>
              <resources>
                <resource>
                  <directory>${basedir}/src/main/resources/directory>
                  <includes>
                    <include>**/*include>
                  includes>
                resource>
              resources>
            configuration>
          execution>
        executions>
      plugin>
      
      <plugin>
        <groupId>org.apache.maven.pluginsgroupId>
        <artifactId>maven-source-pluginartifactId>
        <version>3.0.1version>
        <configuration>
          <attach>trueattach>
        configuration>
        <executions>
          <execution>
            <phase>compilephase>
            <goals>
              <goal>jargoal>
            goals>
          execution>
        executions>
      plugin>
    plugins>
  build>

**注:**部分代码和灵感来源
Java-JSR-269-插入式注解处理器

你可能感兴趣的:(java)