Skip to content

Latest commit

 

History

History
149 lines (107 loc) · 7.99 KB

注解处理器.md

File metadata and controls

149 lines (107 loc) · 7.99 KB

注解处理器

Java 注解基础

java5 引入, 为在代码中添加信息提供了一种新的方式; 注解在一定程度上, 把元数据和源文件结合在一起. 另一种方式是使用配置文件 spring bean声明 包括mybatis mapper (使用xml更好)

元Annotation: 注解的注解:

Annotation提供了为程序元素设置元数据的方法。元数据:描述数据的数据。 包括修饰包、类、构造器、方法、成员变量、参数、局部变量的声明

  1. 注解的作用 1.1 提供用来完整地描述程序所需要的信息,如编译期校验程序信息。 例如@override等 1.2 生成描述符文件,或生成新类的定义。 1.3 减轻编写“样板”代码(配置文件)的负担,可以使用注解自动生成。 1.4 更加干净易读的代码。 1.5 编译期类型检查。

  2. Java内置的注解 Java5内置了一些原生的注解,它们仅次于java.lang包下(不止于此):

@Override,表示当前的方法定义将覆盖超类中的方法。 @Deprecated,标识元素为弃用的,如果程序员使用了注解为它的元素,编译器会发出警告信息。 @SuppressWarnings,关闭不当的编译器警告信息。

  1. 元注解 (注解的注解) @Retention 指定标识的注解如何保存。 @Documented 表明该注解标识的元素所使用的注解应该出现在javadoc中。 @Target 指定哪种JAVA元素可以使用当前定义的注解 @Inherited 指示注释类型被自动继承 @Repeatable (Java8中增加)使用此注解注释的注解,在使用时是可重复使用的

Java 注解处理器

注解的作用

如果没有用来读取注解的工具,那注解将没有任何作用,它也不会比注释更有用 Java 提供两种处理机制: 1) 运行时反射 2) 编译器处理器

注解处理器(编译期对注解的使用)

定义

  1. 注解的处理除了可以在运行时通过反射机制处理外, 还可以在编译期进行处理. 在编译期处理注解时, 会处理到不再产生新的源文件为止, 之后再对所有源文件进行编译;

提供了一组插入式注解处理器的标准API在编译期间对注解进行处理,可以看作是一组编译器的插件,可以读取/修改/添加抽象语法树中的任意元素。

注解处理的两种机制:

  1. Annotation Processing Tool,apt自JDK5产生,JDK7已标记为过期,不推荐使用,JDK8中已彻底删除,自JDK6开始,可以使用Pluggable Annotation Processing API来替换它,apt被替换主要有2点原因。api都在com.sun.mirror非标准包下,还有就是没有集成到javac中,需要额外运行。

  2. Pluggable Annotation Processing API lombok使用这种方式实现,基于JSR 269,自JDK6加入,作为apt的替代方案,它解决了apt的两个问题,javac在执行的时候会调用实现了该API的程序,这样我们就可以对编译器做一些增强,这时javac执行的过程如下

Java5中 提供了apt工具来进行编译期的注解处理. apt 是命令行工具, 与之配套的是一套描述 "程序在 编译时刻的静态结构"的API, Mirror API, 通过Mirror API可以获取到被注解的Java类型元素的信息. 从而提供自定义的处理逻辑. 具体的处理工具交给apt来处理. 编写注解处理器核心是两个类:

  1. 注解处理器 AnnotationProcessor
  2. 注解处理器工厂 (AnnotationProcessorFactory) apt在完成注解处理后, 会自动调用javac来编译处理完成后的源代码; apt工具是oracle提供的私有实现;

Java8 中已经移除APT工具; jdk6中, 将注解处理器这一功能进行了规范化, 形成了 java.annotation.processing的API包, Mirror API则进行封装, 形成javax.lang.model包.

注解处理器的开发进行了简化, 不再单独使用apt工具, 而将此功能集成到javac命令中. (本篇主要将java6的实现, 对apt不做研究)

  1. 实例说明, 介绍Processor使用

2.1 接口 Processor 定义的规范 .... 2.2 抽象类: AbstractProcessor 理解这个对象的调用堆栈 主要方法: init: 保存 ProcessingEnvironment作为成员变量 this.processingEnv = processingEnv; initialized = true; getSupportedOptions (原始的实现也是读取注解的值) 或者注解@SupportedOptions方式, getSupportedVersion 默认 java6版本

Filer接口: 创建新文件, 创建的源文件和类文件将由管理他们的工具javac处理
Messager接口: 注解处理器用此来报告错误消息、警告和其他通知的方式
RoundEnvironment是什么?
    //获取所有编译类元素,并打印,测试用
    Set<? extends Element> elements = roundEnv.getRootElements();
    //获取使用了注解@GenerateInterface的类元素
    roundEnv.getElementsAnnotatedWith(GenerateInterface.class);
    // 工具类
    processingEnv.getElementUtils()

使用java提供的工具javac来执行才能起到作用

    -proc:{none,only} 控制是否执行注释处理和/或编译。-proc:none表示编译期不执行注解处理器; -proc:only表示只执行注解处理器,不进行任何注解之后的编译。

注解处理器执行了三次
    1) 对源代码进行编译, 并执行Processor类生成 XX类 (Processor中定义的类文件)
    2) 对生成的类文件处理, 这一次不再产生新的类
    3) 未能发现新生成的类, 执行结束.

问题:

  1. 注解处理器是在什么时期执行? 如果是编译之前, 那么哪来的对象, 哪来的Processor, 哪来的Class信息
  2. processingEnv是什么, 它的messager是什么?

Java编译器的内核

编译步骤, 参考 https://my.oschina.net/superpdm/blog/129715 a) Parse: 读入一堆*.java源代码,并且把读进来的符号(Token)映射到AST节点上去。 字节流 -> 符号流 -> 映射成AST b) Enter: 把类的定义放到符号表(Symbol Table)中去。 c) Process annotations: 可选的。处理编译单元(compilation units)里面所找到的标记(annotation)。 d) Attribute: 为AST添加属性。这一步包含名字解析(name resolution),类型检测(type checking)和常数折叠(constant fold)。 e) Flow: 为前面得到的AST执行流分析(Flow analysis)操作。这个步骤包含赋值(assignment)的检查和可执行性(reachability)的检查。 f) Desugar: 重写AST, 并且把一些复杂的语法转化成一般的语法。 g) Generate: 生成源文件或者类文件。

注解处理的两种方式

实现原理

首先先简单介绍下Javac的编译过程,大致可以分为3个过程:

  • 解析与填充符号表
  • 插入式注解处理器的注解处理过程
  • 分析与字节码生成过程

首先会进行词法和语法分析,词法分析将源代码的字符流转变为Token集合,关键字/变量名/字面量/运算符读可以成为Token,词法分析过程由com.sun.tools.javac.parserScanner类实现;

语法分析是根据Token序列构造抽象语法树的过程,抽象语法树AST是一种用来描述程序代码语法结构的树形表示,语法树的每一个节点读代表着程序代码中的一个语法结构,例如包/类型/修饰符/运算符/接口/返回值/代码注释等,在javac的源码中,语法分析是由com.sun.tools.javac.parser.Parser类实现,这个阶段产出的抽象语法树由com.sun.tools.javac.tree.JCTree类表示。

经过上面两个步骤编译器就基本不会再对源码文件进行操作了,后续的操作读建立在抽象语法树上。

完成了语法和词法分析后就是填充符号表的过程。符号表是由一组符号地址和符号信息构成的表格。填充符号表的过程由com.sun.tools.javac.comp.Enter类实现。

如前面介绍的,如果注解处理器在处理注解期间对语法树进行了修改,编译器将回到解析与填充符号表的过程重新处理,直到所有插入式注解处理器都没有再对语法树进行修改为止,每一次循环称为一个Round,如下图中的环。

参考文档: https://msd.misuland.com/pd/3127746505234974046