Java-详解javac AbstractProcessor


1️⃣ AbstractProcessor的含义

AbstractProcessor 就是注解处理器 ,在java文件编译成class文件之前,对java文件进行操作,生成代码。(可以参考lombok)


2️⃣ 相关知识点


📝 1. JCTree语法树(AST)(访问者模式)

  • 参考文档

  • JCTree 的一个子类就是java语法中的一个节点,类、方法、字段等这些都被封装成了一个JCTree子类。可以安装JDT AstView(idea插件)可以看到每个class的语法树

在这里插入图片描述


  • JCTree
    • JCStatement (声明节点)
      • 语句块:JCBlock
      • 返回语句:JCReturn
      • 类定义:JCClassDecl
      • 字段定义:JCVariableDecl
    • JCExpression (表达式节点)
      • 赋值语句:JCAssign
      • 变量、类型、关键字:JCIdent
    • JCMethodDecl (方法节点)
      • 无子类
    • JCModifiers (访问标识节点)
      • 无子类

在这里插入图片描述
在这里插入图片描述


📝 2. TreeMaker 语法树构建器

TreeMaker 是 JavaPoet 中的一个重要组件,它用于构建抽象语法树(AST),以生成Java代码。抽象语法树是编程语言中源代码语法结构的树状表示,它将源代码分解为语法单元,并以树的形式表示这些语法单元之间的层次关系。

在 JavaPoet 中,TreeMaker 主要用于创建以下类型的语法树节点:

  1. 类定义
  2. 方法定义
  3. 字段定义
  4. 构造函数定义
  5. 接口定义
  6. 枚举定义
  7. 注解定义
  8. 泛型类型参数定义
  9. 注解参数定义
  10. 方法参数定义
  11. 方法调用
  12. 字段访问
  13. 赋值表达式
  14. 返回语句
  15. 条件语句
  16. 循环语句
  17. 异常捕获语句
  18. 类型转换表达式
  19. 类型字面量表达式
  20. 注解表达式
  21. 数组访问表达式
  22. 字符串拼接表达式
  23. 数组初始化表达式
  24. 等等

TreeMaker 提供了一组方法,用于创建这些不同类型的语法树节点,并设置它们的属性、修饰符、注解等信息。开发者可以使用这些方法来构建所需的语法树,并最终生成Java代码。

例如,要创建一个类定义节点,可以使用以下代码:

TypeSpec classDefinition = TypeSpec.classBuilder("MyClass")
    .addModifiers(Modifier.PUBLIC)
    .addMethod(MethodSpec.methodBuilder("myMethod")
        .addModifiers(Modifier.PUBLIC)
        .returns(void.class)
        .addStatement("System.out.println(\"Hello, World!\")")
        .build())
    .build();

在上述代码中,TypeSpec.classBuilder("MyClass") 创建了一个类定义节点,.addModifiers(Modifier.PUBLIC) 添加了 public 修饰符,.addMethod(...) 添加了一个方法定义节点,最终通过 .build() 构建了一个完整的类定义。

TreeMaker 的灵活性和丰富的方法使得可以轻松地生成复杂的Java代码,并与 JavaPoet 的其他功能一起使用,实现自动化的代码生成。这对于注解处理器等场景非常有用,可以根据注解信息生成相应的Java代码。


📝 3. 关于 jcTree.postreeMaker.pos 的说明

在处理注解时,我们经常需要访问和操作语法树。为了保证操作的准确性和一致性,特别是在生成新的代码元素时,我们需要确保当前节点在语法树中的位置(jcTree.pos)与语法树构建器(treeMaker)中的位置是一致的。

/**
 * @Description 此行注释是必需的。如果缺少此行,在添加 setter 时可能会出现错误。
 * 在处理下一个使用目标注解的地方之前,我们需要首先设置 `pos`。系统中有很多类或方法使用了目标注解,
 * 但在代码生成过程中(即在 `accept` 方法中重写的 `visitClassDef` 为当前类添加或删除元素时),
 * 没有修改 `treeMaker.pos`,这导致即使操作已经移动到下一个类文件,`pos` 仍然保持不变。
 * 实际上,当我在 `visitClassDef` 中将添加或删除操作注释掉时,这个错误不会出现。
 **/
treeMaker.pos = tree.pos;

解释:

  • jcTree.pos:用于指示当前节点在语法树中的位置。
  • treeMaker.pos:在语法树构建器中,表示当前节点的位置。
  • jcTree.defs:表示类的详细定义,包括字段、方法定义等。
  • treeMaker.defs:在语法树构建器中,也表示类的详细定义。

为了保证代码的正确生成和完整性,我们需要确保这些位置和定义在操作时始终保持同步。


3️⃣ AbstractProcessor 解析


📝 1. 对象初始化说明

在进行注解处理时,有几个核心的工具对象,它们通常需要在 init 方法中进行初始化。这些工具对象有助于我们处理注解、生成代码、操作语法树等。下面是这些工具对象的简要说明以及它们通常如何在 init 方法中进行初始化:

  1. Messager: 它提供了一种方式来报告错误、警告以及其他通知。

  2. JavacTrees: 用于获取语法树(JCtree)实例。

  3. TreeMaker: 它是一个工厂类,用于创建语法树节点。

  4. Names: 用于创建名称(name)实例。

  5. elementUtils: 提供了各种实用方法来操作元素。

以下是如何在 init 方法中初始化这些对象的示例:

private Messager messager;
private JavacTrees trees;
private TreeMaker treeMaker;
private Names names;
private Elements elementUtils;

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

📝 2. 注解处理器详解

注解处理器是一个非常强大的工具,它允许开发者在编译时对注解进行处理,进行代码生成或其他操作。下面我们对上述内容进行详细解析:

  1. @SupportedAnnotationTypes(“cc.HelloWorld”)

    • 描述:这个注解定义了注解处理器是为哪些注解设计的。"cc.HelloWorld"HelloWorld 注解的完整类名。
    • 作用:告诉编译器这个处理器是用来处理 cc.HelloWorld 这个注解的。
  2. @SupportedSourceVersion(SourceVersion.RELEASE_7)

    • 描述:这个注解指定了注解处理器支持的Java版本。在这里,它被设置为 JDK 7。
    • 作用:确保处理器只处理指定版本的Java代码。
  3. @AutoService(Processor.class)

    • 描述:这是一个注册服务的注解,使得 AbstractProcessor 的子类能够被编译器自动发现。
    • 作用:自动为注解处理器生成 META-INF/services/javax.annotation.processing.Processor 文件,这是Java服务提供者接口(SPI)的一部分。
  4. annotations

    • 描述:这是传递给 process 方法的注解列表。
    • 作用:告诉处理器这一轮应该处理哪些注解。
  5. roundEnv

    • 描述:它提供了当前处理轮次的环境信息。
    • 作用:可以从中获取被注解标记的元素,然后进一步获取元素的 JCTree 对象,进行代码生成或其他操作。
  6. process 方法的返回值

    • 描述:这个返回值告诉编译器这个注解是否已经被这个处理器处理。
    • 作用:如果返回 true,则这些注解不会再被其他处理器处理;如果返回 false,则这些注解可能会被其他处理器处理。

在这里插入图片描述在这里插入图片描述

注意:每当修改了 AbstractProcessor,为确保所有更改都生效,需要重新编译该类。在Maven项目中,可以先执行clean 命令,然后再执行 compile 命令。如果正在使用IDE进行调试,确保在调试之前先进行 clean 操作。


4️⃣ Javapoet(开源框架用于操作语法树)

常用类和方法是与 JavaPoet 这个库相关的,它是一个用于生成Java代码的工具库,通常用于注解处理器中。下面是常用类和方法的简要解释:

  • MethodSpec:代表一个构造函数或方法的声明。你可以使用它来创建方法或构造函数的定义,包括参数、注解、Javadoc等信息。

  • TypeSpec:代表一个类、接口或枚举的声明。使用它可以创建类、接口或枚举的定义,包括成员变量、方法、内部类等信息。

  • FieldSpec:代表一个成员变量或字段的声明。它用于创建成员变量的定义,包括类型、名称、修饰符等信息。

  • JavaFile:包含一个顶级类的Java文件。你可以将生成的类添加到Java文件中,并最终输出为一个Java源文件。

常用方法和占位符:

  • MethodSpec.addJavadoc("XXX"):可以在方法上方添加注释。

  • MethodSpec.constructorBuilder():用于创建构造函数的构造器。

  • MethodSpec.addAnnotation(Override.class):用于在方法上添加注解。

  • TypeSpec.enumBuilder("XXX"):用于生成一个枚举类型的定义。

  • TypeSpec.interfaceBuilder("HelloWorld"):用于生成一个接口类型的定义。

占位符用于在生成代码时插入字符串、类型或变量名等内容,例如,$S用于插入字符串,$T用于插入类型,$N用于插入变量名等。

这些类和方法可以帮助开发者通过代码生成方式来创建Java类、方法、字段等,从而实现自动化的代码生成和处理。在注解处理器中,它们通常与注解处理器的逻辑一起使用,用于生成与注解相关的代码。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 这个问题说的是在命令行中用"java -version"命令不起作用,不能正常显示Java版本信息的情况。其中,"javac"是Java编译器的命令,与"java -version"没有直接关联。 如果"java -version"不能正常运行,可能是因为Java环境变量未设置或未添加到系统路径中,需要检查一下。如果还是无法解决,可以考虑重装Java或联系技术支持人员寻求帮助。 ### 回答2: Java是一种高级编程语言,被广泛应用于企业级应用开发和互联网应用开发中。它具有跨平台、面向对象、简单易用等优点,其中JVM(Java虚拟机)是Java实现跨平台的核心。Java编译器将Java程序转换成字节码,然后JVM将字节码解释成可以在任何支持JVM的操作系统上运行的本机代码。Java的版本管理是很重要的,因为不同的版本可能具有不同的功能和特性。 java -version命令用于查看Java运行时环境的版本号。而javac命令则是Java编译器的命令,用于将Java源代码编译成字节码文件,然后在JVM中执行。 Java编译器是Java开发工具包(JDK)的一部分,而JDK除了包含Java编译器,也包含了Java运行时环境。 在命令行中使用java -version命令时,JVM会直接读取JRE(Java运行时环境)安装目录下的java.exe文件,并显示当前版本信息。而javac命令需要在JDK的bin目录下执行,因为编译器需要访问JDK中的相关文件和库。 因此,如果在系统中只安装了JRE而没有安装JDK,则无法在命令行中使用javac命令。 另外,在不同的操作系统中,环境变量和文件路径可能会不同,也会影响javac命令的使用。 综上所述,java -version与javac不同,java -version用于查看Java运行时环境的版本信息,而javac用于将Java源代码编译成字节码文件,需要在JDK的bin目录下执行。因此,安装JDK是必须的,以便在命令行中编译和运行Java程序。 ### 回答3: Java -version命令是用于显示安装在计算机上的Java版本信息的命令,它可以显示Java版本号、Java运行时环境版本号以及Java开发工具包版本号等信息。 而javac命令则是Java编译器的命令,它用于将Java源代码文件(扩展名为.java)编译成Java字节码文件(扩展名为.class),Java字节码文件可以被Java虚拟机(JVM)执行。 因为Java -version命令是用于显示Java版本信息的命令,与Java编译器的命令javac并没有直接的关联,所以在命令行中输入java -version可以成功显示Java版本信息,而输入javac则不能。 此外,要使用javac命令,需要首先安装并配置好Java开发工具包(Java Development Kit,JDK),因为javac命令是JDK中的一个工具。而Java -version命令则只需要安装Java运行时环境(Java Runtime Environment,JRE),即可使用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yueerba126

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值