前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AbstractProcessor相关的API记录

AbstractProcessor相关的API记录

作者头像
天涯泪小武
发布2022-12-23 08:40:48
4230
发布2022-12-23 08:40:48
举报
文章被收录于专栏:SpringCloud专栏SpringCloud专栏

java文件操作相关的两个类: JCTree 树节点、TreeMaker 树节点构建器。

JCTree

JCTree的一个子类就是java语法中的一个节点,类、方法、字段等这些都被封装成了一个JCTree子类。

JCTree详细的介绍:抽象语法树AST的全面解析(二) - 简书

TreeMaker

TreeMaker 里的方法用于构建JCTree某个子类,例:treeMaker.MethodDef(…)返回值是JCTree的子类JCMethodDecl(方法节点)

TreeMaker创建各种节点的方法如下:

代码语言:javascript
复制
// public访问修饰符
JCTree.JCModifiers publicDot = treeMaker.Modifiers(Flags.PUBLIC);
// void关键字
JCTree.JCPrimitiveTypeTree voidDot = treeMaker.TypeIdent(TypeTag.VOID);
// 空语句
JCTree.JCBlock emptyDot = treeMaker.Block(0, List.nil());
// 一个无参构造方法
JCTree.JCMethodDecl noArgsMethod = treeMaker.MethodDef(
                    publicDot,
                    names.fromString("<init>"),
                    voidDot ,
                    List.nil(),
                    List.nil(),
                    List.nil(),
                    emptyDot,
                    null);



// this关键字
JCTree.JCIdent thisDot = treeMaker.Ident(names.fromString("this"));
// userPassword     (Name类型可以是方法名、字段名、变量名、类名)
Name userPasswordName = names.fromString("userPassword");
// this.userPassword  (访问当前方法中的字段userPassword)
JCTree.JCFieldAccess thisUserPassword = treeMaker.Select(thisDot , userPasswordName);
// userPassword 变量
JCTree.JCIdent userPassword = treeMaker.Ident(userPasswordName);
// this.userPassword = userPassword       (treeMaker.Apply()以及treeMaker.Assign()需要外面包一层treeMaker.Exec())
treeMaker.Exec(treeMaker.Assign(thisUserPassword, userPassword)); 

 如以下代码是给一个方法新增一句话System.out.println("hello world")

代码语言:javascript
复制
package jst;

import com.google.auto.service.AutoService;
import com.sun.tools.javac.model.JavacElements;
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.util.Context;
import com.sun.tools.javac.util.List;

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

/**
 * @author wuweifeng wrote on 2022/12/21
 * @version 1.0
 */
@SupportedAnnotationTypes("jst.HelloWorld")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elements = roundEnv.getRootElements();
        final Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        final JavacElements elementUtils = (JavacElements) processingEnv.getElementUtils();
        final TreeMaker treeMaker = TreeMaker.instance(context);

        for (Element element : roundEnv.getElementsAnnotatedWith(HelloWorld.class)) {
            JCTree.JCMethodDecl jcMethodDecl = (JCTree.JCMethodDecl) elementUtils.getTree(element);

            treeMaker.pos = jcMethodDecl.pos;
            jcMethodDecl.body = treeMaker.Block(0, 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("Hello, world!!!")
                                    )
                            )
                    ),
                    jcMethodDecl.body
            ));
        }
        return false;
    }

}

使用时

代码语言:javascript
复制
@HelloWorld
    public static void main(String[] args) {
        System.out.println("demo打印");
    }

加上了@HelloWorld注解后,运行就会打印

现在针对MyProcessor类里的内容做个解释。

jcMethodDecl.body即为方法体,利用treemaker的Block方法获取到一个新方法体,将原来的替换掉。就达到了修改方法体的目的了。这里的Block方法有两个参数,重点要关注的是第二个参数,也就是具体的方法体内容。它是一个List类型的参数,List里面每一个元素就代表一个语句块,比如,例子中有两块语句,第一块是我们织入的代码块:System.out.println("Hello, world!!!");用treeMaker.Exec()来实现,第二块是原来的代码块:jcMethodDecl.body。这块的代码是用户原本的代码,我们直接放进来就行。这个List是有顺序的,谁的顺序在前,谁最终生成的代码块就在前,比如这里我们织入的代码在原来的代码块之前,所以最终生成System.out.println("Hello, world!!!");语句就在该方法的第一行位置。

重点关注的应该是treeMaker.Exec()这个方法,这个方法帮助我们最终生成了System.out.println("Hello, world!!!");这条语句,它的参数是treeMaker.Apply这个方法的返回结果,这个方法的第二个参数,也就是最终实现了输出System.out.println("Hello, world!!!")的东西。

这里面用到了两个方法,一个是treeMaker.Select(生成具体的方法),一个是treeMaker.Literal(方法的参数)。treeMaker.Select里面套了很多层,对比两种写法的区别,你也能明白,这是为了写出多级方法的做法,多级方法的第一级以treeMaker.Ident开始,然后一层套一层,直到整个方法结束。

如何debug processor代码

a)idea右上角,Edit Configurations

b)点击左上角的+号,选择Remote,或者如图的Remote JVM debug

c)随便起一个名字,比如ProcessorDebug,如图,点击确定。

d)进入Terminal界面,输入mvnDebug clean install,回车,不出意外的话,会出现如图的提示

f)process里面打上断点,然后点击debug按钮,便命中了断点

异常情况 

如果在rebuild project时出现:java: java.lang.ClassCastException: com.sun.proxy.$Proxy26 cannot be cast to com.sun.tools.javac.processing.JavacProcessingEnvironment

则在设置里添加如下内容:

-Djps.track.ap.dependencies=false

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-12-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JCTree
  • TreeMaker
  • 如何debug processor代码
  • 异常情况 
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档