编译时注解

很早之前我便会了使用编译时注解生成新的文件,类似安卓中黄油刀等,但是我的目标不是生成新的类,而是在编译时修改字节码做出类似Lombok那样的插件最近抽空找了一些资料终于写出了自己的编译时字节码修改注解

参考:https://liuyehcf.github.io/2018/02/02/Java-JSR-269-%E6%8F%92%E5%85%A5%E5%BC%8F%E6%B3%A8%E8%A7%A3%E5%A4%84%E7%90%86%E5%99%A8/
关于对应的mavan配置我前面写过,网上也有一大堆,这里就不再重复了
注意编写前必须先引入jdk自带的tools.jar文件

<dependencies>
        <dependency>
            <groupId>jdk.tools</groupId>
            <artifactId>jdk.tools</artifactId>
            <version>${java.version}</version>
            <scope>system</scope>
            <systemPath>${env.JAVA_HOME}/lib/tools.jar</systemPath>
        </dependency>
    </dependencies>

NoArgsConstructorProcessor

@SupportedAnnotationTypes("annotations.NoArgsConstructor")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class NoArgsConstructorProcessor extends AbstractProcessor {

    private JavacTrees javacTrees;
    private TreeMaker treeMaker;
    private Names names;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        javacTrees=JavacTrees.instance(context);//把 Element 转为 JCTree
        treeMaker=TreeMaker.instance(context);// 构造语法树节点
        names=Names.instance(context);// 用于产生标识符
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(NoArgsConstructor.class);
        elements.forEach((Consumer<Element>) element -> javacTrees.getTree(element).accept(new TreeTranslator(){
            @Override//采用访问者模式  访问类的定义
            public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                super.visitClassDef(jcClassDecl);
                if(!hasNoArgsConstructor(jcClassDecl)){
                    // 注意  List每次都会返回新对象
                    jcClassDecl.defs=jcClassDecl.defs.append(createNoArgsConstructor(jcClassDecl));
                }
            }
        }));
        return true;
    }

    private JCTree.JCMethodDecl createNoArgsConstructor(JCTree.JCClassDecl jcClassDecl) {
        treeMaker.pos=jcClassDecl.pos;//注意 偏移必须对准
        JCTree.JCBlock block=treeMaker.Block(0, List.nil());//空代码块
        return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC),//访问标识
                names.fromString("<init>"),//方法名
                treeMaker.TypeIdent(TypeTag.VOID),//返回值类型
                List.nil(),//泛型列表
                List.nil(),//参数列表
                List.nil(),//抛出的异常
                block,//代码块
                null);
    }

    // 判断是否已经有默认的空参构造器
    private boolean hasNoArgsConstructor(JCTree.JCClassDecl jcClassDecl) {
        for (JCTree jcTree:jcClassDecl.defs){
            if(jcTree.getKind().equals(Tree.Kind.METHOD)){
                JCTree.JCMethodDecl methodDecl= (JCTree.JCMethodDecl) jcTree;
                if(methodDecl.getName().toString().equals("<init>")&&methodDecl.getParameters().isEmpty()){
                    return true;
                }
            }
        }
        return false;
    }

}

AllArgsConstructorProcessor

@SupportedAnnotationTypes("annotations.AllArgsConstructor")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class AllArgsConstructorProcessor extends AbstractProcessor {

    private JavacTrees javacTrees;
    private TreeMaker treeMaker;
    private Names names;

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

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(AllArgsConstructor.class);
        elements.forEach(new Consumer<Element>() {
            @Override
            public void accept(Element element) {
                javacTrees.getTree(element).accept(new TreeTranslator(){
                    @Override
                    public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                        super.visitClassDef(jcClassDecl);
                        jcClassDecl.defs=jcClassDecl.defs.append(createAllArgsConstructor(jcClassDecl));
                    }
                });
            }
        });
        return true;
    }

    private JCTree.JCMethodDecl createAllArgsConstructor(JCTree.JCClassDecl jcClassDecl) {
        treeMaker.pos=jcClassDecl.pos;
        List<JCTree.JCVariableDecl> param=getParam(jcClassDecl);
        // ListBuffer 跟类似  Java原生List  可以直接使用append  也可使用  toList 转为  tools.jar下的List
        ListBuffer<JCTree.JCStatement> buffer=new ListBuffer<>();
        param.forEach(variableDecl -> buffer.append(
                treeMaker.Exec(treeMaker.Assign(//赋值操作
                treeMaker.Select(treeMaker.Ident(names.fromString("this")),variableDecl.name),// this.<name>
                treeMaker.Ident(variableDecl.name)))));//<param.name>
        JCTree.JCBlock block = treeMaker.Block(0, buffer.toList());
        return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC),names.fromString("<init>"),
                treeMaker.TypeIdent(TypeTag.VOID), List.nil(),param, List.nil(),block,null);
    }

    // 获取所有非静态Final变量
    private List<JCTree.JCVariableDecl> getParam(JCTree.JCClassDecl jcClassDecl) {
        ListBuffer<JCTree.JCVariableDecl> buffer = new ListBuffer<>();
        for (JCTree tree:jcClassDecl.defs){
            if(tree.getKind().equals(Tree.Kind.VARIABLE)){
                JCTree.JCVariableDecl variableDecl= (JCTree.JCVariableDecl) tree;
                Set<Modifier> flags = variableDecl.mods.getFlags();
                if(!flags.contains(Modifier.FINAL)&&!flags.contains(Modifier.STATIC)){
                    buffer.append(treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER),variableDecl.name,
                            variableDecl.vartype,null));
                }
            }
        }
        return buffer.toList();
    }

}

DataProcessor

@SupportedAnnotationTypes("annotations.Data")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class DataProcessor extends AbstractProcessor {

    private JavacTrees javacTrees;
    private TreeMaker treeMaker;
    private Names names;

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

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        roundEnv.getElementsAnnotatedWith(Data.class).forEach(new Consumer<Element>() {
            @Override
            public void accept(Element element) {
                javacTrees.getTree(element).accept(new TreeTranslator(){
                    @Override
                    public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                        super.visitClassDef(jcClassDecl);
                        List<JCTree.JCVariableDecl> param=getParam(jcClassDecl);
                        param.forEach(variableDecl -> {
                            //  添加  getter  setter
                            jcClassDecl.defs=jcClassDecl.defs.append(createGetter(variableDecl));
                            jcClassDecl.defs=jcClassDecl.defs.append(createSetter(variableDecl));
                        });
                    }
                });
            }
        });
        return true;
    }

    private JCTree.JCMethodDecl createSetter(JCTree.JCVariableDecl variableDecl) {
        treeMaker.pos=variableDecl.pos;
        JCTree.JCBlock block = treeMaker.Block(0, List.of(treeMaker.Exec(treeMaker.Assign(
                treeMaker.Select(treeMaker.Ident(names.fromString("this")), variableDecl.name),
                treeMaker.Ident(variableDecl.name)))));
        return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC),
                names.fromString(toSetter(variableDecl.getName().toString())),treeMaker.TypeIdent(TypeTag.VOID),
                List.nil(),List.of(variableDecl),List.nil(),block,null);
    }

    private String toSetter(String name) {
        return "set"+name.substring(0,1).toUpperCase()+name.substring(1);
    }

    private JCTree.JCMethodDecl createGetter(JCTree.JCVariableDecl variableDecl) {
        treeMaker.pos=variableDecl.pos;
        JCTree.JCStatement statement=treeMaker.Return(// return
                treeMaker.Select(treeMaker.Ident(names.fromString("this")),variableDecl.name));// this.<name>
        JCTree.JCBlock block = treeMaker.Block(0, List.of(statement));
        return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC),
                names.fromString(toGetter(variableDecl.getName().toString())),variableDecl.vartype,List.nil(),
                List.nil(),List.nil(),block,null);
    }

    private String toGetter(String name) {
        return "get"+name.substring(0,1).toUpperCase()+name.substring(1);
    }

    private List<JCTree.JCVariableDecl> getParam(JCTree.JCClassDecl jcClassDecl) {
        ListBuffer<JCTree.JCVariableDecl> buffer = new ListBuffer<>();
        for (JCTree tree:jcClassDecl.defs){
            if(tree.getKind().equals(Tree.Kind.VARIABLE)){
                JCTree.JCVariableDecl variableDecl= (JCTree.JCVariableDecl) tree;
                Set<Modifier> flags = variableDecl.mods.getFlags();
                if(!flags.contains(Modifier.FINAL)&&!flags.contains(Modifier.STATIC)){
                    buffer.append(treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER),variableDecl.name,
                            variableDecl.vartype,null));
                }
            }
        }
        return buffer.toList();
    }

}

学习笔记

文档地址

https://liuyehcf.github.io/2018/02/02/Java-JSR-269-%E6%8F%92%E5%85%A5%E5%BC%8F%E6%B3%A8%E8%A7%A3%E5%A4%84%E7%90%86%E5%99%A8/

准备

必须导入jdk的tools.jar文件

<dependency>
    <groupId>jdk.tools</groupId>
    <artifactId>jdk.tools</artifactId>
    <version>${java.version}</version>
    <scope>system</scope>
    <systemPath>${env.JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>

添加打包插件防止死循环

<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <!-- Disable annotation processing for ourselves.-->
                <compilerArgument>-proc:none</compilerArgument>
            </configuration>
        </plugin>
    </plugins>
</build>

在MATE-INF/services下的javax.annotation.processing.Processor注册注解处理器

基本概念

Java编译过程

解析与填充符号表过程

插入式注解处理器的注解处理过程

分析与字节码生成过程

JCTree

基类,属性**pos**指定在语法树中的位置,因此各个语法组件无法直接new,需要使用TreeMake生成

常见子类

  1. JCStatement:声明 语法树节点,常见的子类如下
    • JCBlock:语句块语法树节点
    • JCReturn:return语句语法树节点
    • JCClassDecl:类定义语法树节点
    • JCVariableDecl:字段/变量定义语法树节点
  2. JCMethodDecl:方法定义语法树节点
  3. JCModifiers:访问标志语法树节点
  4. JCExpression:表达式 语法树节点,常见的子类如下
    • JCAssign:赋值语句语法树节点
    • JCIdent:标识符语法树节点,可以是变量,类型,关键字等等

TreeMaker

用于创建语法树节点,会为创建的节点设置pos,必须使用上下文相关的不能直接new

TreeMaker.Modifiers

访问标识符 flags:访问标志 annotations:注解列表

TreeMaker.ClassDef

用于定义类 mods:访问标志 name:类名 typarams:泛型参数列表 extending:父类 implementing:接口列表 defs:类定义的详细语句,包括字段,方法定义等等

TreeMaker.MethodDef

用于定义方法 mods:访问标志 name:方法名 restype:返回类型 typarams:泛型参数列表 params:参数列表 thrown:异常声明列表 body:方法体 defaultValue:默认方法(可能是interface中的那个default) m:方法符号 mtype:方法类型。包含多种类型,泛型参数类型、方法参数类型,异常参数类型、返回参数类型

TreeMaker.VarDef

用于创建字段或变量 mods:访问标志 vartype:类型 init:初始化语句 v:变量符号

TreeMaker.Ident

用于创建标识符

TreeMaker.Return

用于创建return语句

TreeMaker.Select

获取对象属性 selected:.运算符左边的表达式 selector:.运算符右边的名字

TreeMaker.NewClass

用于创建new语法树 encl:不太明白此参数含义 typeargs:参数类型列表 clazz:待创建对象的类型 args:参数列表 def:类定义

TreeMaker.Apply

方法调用 typeargs:参数类型列表 fn:调用语句 args:参数列表

TreeMaker.Assign

创建赋值语句 lhs:赋值语句左边表达式 rhs:赋值语句右边表达式

TreeMaker.Exec

用于执行可执行语法树 JCExpressionStatement

TreeMaker.Block

用于创建组合语句语法树节点 flags:访问标志 stats:语句列表

com.sun.tools.javac.util.List

特殊List每次添加对象会返回新List

com.sun.tools.javac.util.ListBuffer

与Java原生list类似且提供转为List的方法

例子

创建方法

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

方法参数

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

赋值语句

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

new对象

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

方法调用

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

扩展

Java编译器编译过程

Parser:将符号流映射成一个AST

Enter:找到当前范围的所有定义并转换为符号

Process Annotations:可选,处理编译时注解,不能使用反射操作

Attribute:为生成的AST添加属性

Flow:对数据流进行检查

Desugar:还原去除语法糖

Generate:生成类文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值