JDK APT(Annotation Processing Tool) 编译时注解处理

embedded/2024/10/21 3:27:40/

博文目录

文章目录

  • javac
  • Annotation Processing
    • How Annotation Processing Works
    • Compilation Environment and Runtime Environment
  • maven-compile-plugin
  • 对 Maven pom 中配置注解处理器的理解
  • Lombok, MapStruct, MyBatis-Flex 说明
  • Maven 配置顺序
    • 编译正常
    • 编译错误


javac_4">javac

The javac Command - processor-path

先看下面几个 javac 命令参数的说明

  • -proc:[none, only, full]
    • Controls whether annotation processing and compilation are done. -proc:none means that compilation takes place without annotation processing. -proc:only means that only annotation processing is done, without any subsequent compilation. If this option is not used, or -proc:full is specified, annotation processing and compilation are done.
    • 控制是否完成注解处理和编译。-proc:none 表示在没有注解处理的情况下进行编译。-proc:only 意味着只进行注解处理,而不进行任何后续编译。如果未使用此选项,或指定 -proc:full 则完成注解处理和编译。
  • -processor class1[,class2,class3...]
    • Names of the annotation processors to run. This bypasses the default discovery process.
    • 要运行的注解处理器的名称。这绕过了默认的发现进程。
  • --processor-module-path path
    • Specifies the module path used for finding annotation processors.
    • 指定用于查找注解处理器的模块路径。
  • --processor-path path or -processorpath path
    • Specifies where to find annotation processors. If this option is not used, then the class path is searched for processors.
    • 指定在何处找到注解处理器。如果未使用此选项,则在类路径中搜索处理器。

大致意思是, javac 命令有 注解处理编译 的功能, 常规流程是先处理注解, 待完成后再执行编译操作

默认情况下, javac 会从类路径中搜索注解处理器(如何搜索下面说), 然后执行其注解处理逻辑, 最后执行编译流程

这里说的注解处理功能就是 APT, 全称 Annotation Processing Tool. 具体的注解处理逻辑由注解处理器定义, 通常都是生成一些新的源码, 源码参与后续编译, 从而增强对应类与功能, 常见的有 Lombok, MapStruct, MyBatis-Flex 等

Annotation Processing

The javac Command - Annotation Processing

javac 编译命令提供了注解处理的直接支持, 相关 API 被定义在 javax.annotation.processing 和 javax.lang.model 包和子包中

How Annotation Processing Works

除非使用 -proc:none 选项禁用注解处理,否则编译器将搜索任何可用的注解处理器。可以使用 -processorpath 选项指定搜索路径。如果未指定路径,则使用用户类路径。处理器通过搜索路径上名为 META-INF/services/javax.annotation.Processing.Processor 的服务提供者配置文件定位(就是判断各个 jar 包中是否有这个文件)。这些文件应该包含要使用的任何注解处理器的名称,每行列出一个。或者,也可以使用 -processor 选项显式指定处理器(就是说 -processorpath 和 -processor 两个参数二选一)。

在扫描命令行上的源文件和类以确定存在哪些注解之后,编译器将查询处理器以确定它们处理哪些注解。找到匹配的注解时调用处理器。处理器可以声明它所处理的注解,在这种情况下,不会进一步尝试为这些注解寻找任何处理器(就是说一旦找到某注解的一个处理器就不再继续寻找)。在所有注解都被声明处理之后,编译器不会搜索其他处理器(就是说如果有多余的处理器没有匹配到注解, 这些处理器也不会被调用了)。

如果任何处理器生成新的源文件,那么就会进行另一轮注解处理: 扫描所有新生成的源文件,并像以前一样处理注解。在前几轮调用的任何处理器也在后面的所有轮调用。这种情况一直持续到不生成新的源文件为止。

在没有生成新源文件的情况下进行一轮后,将最后一次调用注解处理器,以便它们有机会完成剩余的任何工作。最后,除非使用 -proc:only 选项,否则编译器将编译原始文件和所有生成的源文件。

如果使用注解处理器生成要包含在编译中的其他源文件,则可以指定用于新生成的文件的默认模块,以便在不生成模块声明时使用。在这种情况下,使用 --default-module-for-create-files 选项。

Compilation Environment and Runtime Environment

Javac 分析源文件和之前编译的类文件中的声明时,使用的编译环境不同于用来执行 javac 本身的运行时环境。尽管很多 javac 选项和 Java 启动程序的类似命名选项之间存在一定的相似性,比如 --class-path--module-path 等等,但是重要的是要理解,一般来说 javac 选项只影响源文件编译的环境,并不影响 javac 本身的操作。

在使用注解处理器时,编译环境和运行时环境之间的区别是显著的。虽然注解处理器处理编译环境中存在的元素(声明) ,但是注解处理器本身是在运行时环境中执行的。如果注解处理器依赖于不在模块中的库,那么可以将这些库以及注解处理器本身放在处理器路径上。(–processor-path 选项)。如果注解处理器及其依赖项位于模块中,则应改为使用处理器模块路径。(–processor-module-path 选项)。当这些不足够时,可能需要提供进一步的运行时环境配置。

  • 如果从命令行调用 javac,则可以通过在选项前面加上 -J 将选项传递给底层运行时。
  • 您可以直接启动 Java 虚拟机的一个实例,并使用命令行选项和 API 来配置一个环境,在这个环境中可以通过其中一个 API 调用 javac。

maven-compile-plugin

Maven 编译插件提供了 APT 支持

  • annotationProcessorPaths
    • 指定注解处理器的多个搜索路径。如果指定了,编译器将只在这些类路径元素中检测注解处理器。如果省略,则使用默认类路径来检测注解处理器。检测本身依赖于 annotationProcessors 配置。
    • 每个类路径元素都使用它们的 Maven 坐标 (groupId、 artifactId、 version)指定。
  • annotationProcessorPathsUseDepMgmt
    • 在解析注解处理器路径的传递依赖关系时,是否使用 Maven 依赖关系管理部分。
    • 此标志不启用/禁用从依赖项管理部分解析注解处理程序路径版本的能力。它只影响那些顶级路径的传递依赖关系的解析。
  • annotationProcessors
    • 要运行的注解处理程序的名称。只适用于 JDK 1.6 +,如果没有设置,则应用默认的注解处理程序发现过程。

对 Maven pom 中配置注解处理器的理解

javac 执行过程中, 先扫描源文件以确定存在哪些注解, 之后编译器在类路径中逐个检索处理器以确定能处理哪些注解, 然后逐个调用找到的处理器, 循环这些过程直到不再生成新的源文件. 最后才是执行编译

  • 不指定编译时的注解相关参数, 编译器会在所有类路径中, 逐个找到注解处理器并执行
  • 指定注解处理器的检索路径时, 编译器只会在指定的类路径中, 逐个找到注解处理器并执行

换句话说, 只要把注解处理器配置到 dependencies 标签中即可, 并不需要特地去配置 maven-compile-plugin 的 annotationProcessorPaths, 但是一旦配置了 annotationProcessorPaths, 就需要把所有涉及到的注解处理器都配置过来, 因为编译时只会在配置的类路径中查找注解处理器

Lombok, MapStruct, MyBatis-Flex 说明

MyBatis-Flex 和 Lombok、Mapstruct 整合
MapStruct - Can I use MapStruct together with Project Lombok?

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><annotationProcessorPaths><!-- Lombok 相关注解处理器 --><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${org.projectlombok.version}</version></path><!-- Lombok 版本大于等于 1.18.16 时使用 MapStruct 需加入下述依赖 --><path><groupId>org.projectlombok</groupId><artifactId>lombok-mapstruct-binding</artifactId><version>${lombok-mapstruct-binding.version}</version></path><!-- MapStruct 相关注解处理器 --><path><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>${org.mapstruct.version}</version></path><!-- MyBatis-Flex 相关注解处理器 --><path><groupId>com.mybatis-flex</groupId><artifactId>mybatis-flex-processor</artifactId><version>${mybatis-flex.version}</version></path></annotationProcessorPaths></configuration>
</plugin>

MyBatis-Flex 推荐上述配置方式, 用于与 Lombok, MapStruct 一起使用, Lombok 放最前, 其次是 MapStruct, 然后是 MyBatis-Flex

MapStruct 中关于如何与 Lombok 一起使用, 做出了说明, MapStruct 需要用到 Getter Setter 方法, 所以要在 Lombok 之后工作

Lombok 是一个注解处理器,它(除了其他功能之外)将 getter 和 setter 添加到编译 bean 类的 AST (抽象语法树)中。对 AST 的修改是 Java 注解处理 API 无法预见的,所以 Lombok 和 MapStruct 需要一些技巧来使它们一起工作。基本上,MapStruct 要等到 Lombok 完成所有修改之后,才能为增强了 Lombok 的 bean 生成 mapper 类。

如果您正在使用 Lombok 1.18.16 或更新版本,您还需要添加 lombok-mapstruct-binding 注解处理器以使 Lombok 和 MapStruct 协同工作。

MapStruct 给出的示例代码中, annotationProcessorPaths 中配置的 Lombok, MapStruct 并不需要考虑先后顺序

<?xml version="1.0" encoding="UTF-8"?>
<!--Copyright MapStruct Authors.Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.mapstruct.examples.lombok</groupId><artifactId>mapstruct-examples-lombok</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><org.mapstruct.version>1.6.0.Beta1</org.mapstruct.version><org.projectlombok.version>1.18.30</org.projectlombok.version><lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version></properties><dependencies><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>${org.mapstruct.version}</version></dependency><!-- lombok dependencies should not end up on classpath --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${org.projectlombok.version}</version><scope>provided</scope></dependency><!-- IntelliJ pre 2018.1.1 requires the mapstruct processor to be present as provided dependency -->
<!--        <dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>${org.mapstruct.version}</version><scope>provided</scope></dependency>--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope><version>4.13.1</version></dependency></dependencies><build><pluginManagement><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><!-- See https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html --><!-- Classpath elements to supply as annotation processor path. If specified, the compiler   --><!-- will detect annotation processors only in those classpath elements. If omitted, the     --><!-- default classpath is used to detect annotation processors. The detection itself depends --><!-- on the configuration of annotationProcessors.                                           --><!--                                                                                         --><!-- According to this documentation, the provided dependency processor is not considered!   --><annotationProcessorPaths><path><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>${org.mapstruct.version}</version></path><path><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${org.projectlombok.version}</version></path><path><groupId>org.projectlombok</groupId><artifactId>lombok-mapstruct-binding</artifactId><version>${lombok-mapstruct-binding.version}</version></path></annotationProcessorPaths></configuration></plugin></plugins></pluginManagement></build>
</project>

Maven 配置顺序

新建一个测试的 Maven 项目, 用于测试 Lombok 和 MapStruct 是否受 pom.xml 配置的影响

提供 User 和 UserDto 两个类, 提供 UserMapper 用于 User 转 UserDto

通过修改 lombok 和 mapstruct-processor 的先后位置, 判断先后顺序是否影响注解处理器工作

结果是, 会影响

  • 当只配置 dependencies 没配置 maven-compile-plugin 的 annotationProcessorPaths
    • 当 lombok 在 mapstruct-processor 前时, 可正常编译通过
java">@Getter
@Setter
@ToString
public class User {private String username;
}@Getter
@Setter
@ToString
public class UserDto {private String username;
}@Mapper
public interface UserMapper {UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);@Mapping(source = "username", target = "username")UserDto userToUserDto(User user);
}

编译正常

<dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.32</version></dependency><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>1.5.5.Final</version></dependency><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.5.5.Final</version></dependency>
</dependencies>

编译错误

<dependencies><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>1.5.5.Final</version></dependency><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.5.5.Final</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.32</version></dependency>
</dependencies>
No property named "username" exists in source parameter(s). Type "User" has no properties.

http://www.ppmy.cn/embedded/38771.html

相关文章

老牌Git客户端 mac软件 SmartGit 汉化教程 及安装教程

SmartGit for Mac一款 Git 版本控制系统的图形化客户端程序&#xff0c;它能在您的工作上满足您的需求&#xff0c;smartgit是一个企业级的Git、Mercurial、以及Subversion图形化客户端软件&#xff0c;功能非常强大&#xff0c;它可以简单快速的实现Git及Mercurial中的版本控制…

【qt】容器的用法

容器目录 一.QVertor1.应用场景2.增加数据3.删除数据4.修改数据5.查询数据6.是否包含7.数据个数8.交换数据9.移动数据10.嵌套使用 二.QList1.应用场景2.QStringList 三.QLinkedList1.应用场景2.特殊点3.用迭代器来变量 四.QStack1.应用场景2.基本用法 五.QQueue1.应用场景2.基本…

python-调用js代码

安装 pip3 install PyExecJS -i https://pypi.tuna.tsinghua.edu.cn/simple/ 查看node引擎 print(execjs.get()) 必须要是ExternalRuntime(Node.js (V8))&#xff0c;不是要重新安装pycharm 不带参数 import execjs js_data function aa(){return 123}#compile 编译js js e…

Qt | QValidator 抽象类(验证器)及其子类|QDoubleValidator 类|QIntValidator 类

01、上节回顾 Qt | QComboBox(组合框)Qt | QLineEdit 类(行编辑器)02、QValidator 1、QValidator 类直接继承自 QObject 类,且是一个抽象类,因此具体功能主要由其子类来实现,或者子类化该类实现自定义的验证器。 2、验证器的作用是验证用户输入的

2025第23届太原煤炭(能源)工业技术与装备展览会

第二十三届太原煤炭&#xff08;能源&#xff09;工业技术与装备展览会 邀 请 函 指导单位&#xff1a; 中国煤炭工业协会 主办单位&#xff1a;山西省煤炭工业协会 承办单位&#xff1a;太原奇新展览有限公司 展览时间&#xff1a;2025年4月22-24日 展览地点&#xff1a…

代码随想录Day23

530.二叉搜索树的最小绝对差 题目&#xff1a;530. 二叉搜索树的最小绝对差 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;需要两两做差&#xff0c;层序遍历之后套两个for循环&#xff1f;应该可以利用一下二叉搜索树的特点&#xff0c;遍历每一个节点&#xff0c…

C#中的引用参数

在C#中&#xff0c;引用参数使得方法能够修改调用者的变量值&#xff0c;而不是修改变量的副本。为了使用引用参数&#xff0c;方法的参数前需要加上ref关键字。 以下是一个使用引用参数的简单例子&#xff1a; public void Swap(ref int x, ref int y) {int temp x;x y;y …

调用 gradio 创建聊天网页报错(使用远程服务器)

文章目录 写在前面1、使用默认IP地址&#xff08;失败&#xff09;2、使用本地IP地址&#xff08;失败&#xff09;3、使用远程服务器IP地址&#xff08;成功&#xff09; 写在前面 我复现了github上的 llama-chinese 的工作 使用的是 llama2&#xff0c;环境配置是在远程服务…