注解和注解处理器

来源:互联网 发布:游戏原画网络班 编辑:程序博客网 时间:2024/05/29 07:49

注解机制 = 注解 + 注解处理器

注解定义规则, 注解处理器实现规则定义的处理.

注解

注解是一种语言. 其使用的符号系统如下:

使用语法

@Annotation@Annotation2(Element1 [, Element2]) // Element: name = valueClass/Method/Field
  • 注解标记于 @Target 元注解指定的语言结构上方(见下文元注解)
  • 注解可指定元素, 元素为名值对, 元素被注解处理器使用; 特殊地, 没有元素的注解称为标记注解

定义语法

@Meta Annotationpublic @interface ANNOTATION_NAME {    // 元素定义(可选)    public ANNOTATION_DATA_TYPE ELEMENT_NAME() [default DEFAULT_VALUE];}
  • 注解定义语法和接口类似, 但用 @interface 保留字
  • 注解元素定义语法和方法类似, 但可指定默认值
  • 注解元素可用类型包括
    • 基本数据类型(不允许使用包装类型)
    • String
    • 枚举类型
    • Class
    • 注解类型
    • 以上类型的数据类型
  • 注解需要用元注解描述

元素值和注解元素默认值不能为 null, (惯例)通常使用负数或空字符串表示某个元素不存在.

元注解专用于定义注解, Java 提供了四种:

  • @Target 定义注解目标, 可以注解的目标定义在 ElementType 枚举中, 包括
    • PACKAGE 包声明
    • TYPE 类、接口、枚举声明
    • CONSTRUCTOR 构造器声明
    • FIELD 字段声明
    • METHOD 方法声明
    • PARMETER 参数声明
    • LOCAL_VARIABLE 局部变量声明
  • @Retention 定义注解在哪些阶段是有效的, 阶段定义在 RetentionPolicy 枚举中, 包括
    • SOURCE 编译时无效
    • CLASS 编译时有效
    • RUNTIME 编译时和运行时都有效
  • @Documented 指定该注解将包含在 Javadoc 中
  • @Inherited 允许子类继承父类中的注解

说明

  • 解释@Inherited注解: 子类继承父类, 且父类被注解, 若注解子类的注解定义中包含 @Inherited, 则子类也继承父类的注解
  • @Target 注解值
    • 没有 @Target: 表示该注解可用于注解 ElementType 中定义的任意元素
    • @Target 单个值, 如 @Target(ElementType.TYPE): 表示该注解仅用于注解特定的目标, 比如这里仅用于注解类型
    • @Target 多个值, 如 @Target(ElementType.TYPE, ElementType.FIELD): 表示该注解能注解指定范围的目标, 比如这里可注解类型和字段

预定义的注解

Java 内置了3个功能性注解和4个元注解(介绍见上文), 3个功能性注解:

  • @Override 告诉编译器“我想重写方法”
  • @Deprecated 告诉编译器”告知使用者该目标过时”
  • @SuppressWarnings 告诉编译器”你给出的警告不恰当, 关闭警告”

注解处理器

注解处理器可以在编译器进行注解处理, 可以在运行时通过反射API进行注解处理.

注解处理器在 Java 5 引入, 但那时并没有标准化的 API 可用, 需通过 apt(Annotation Processing Tool)结合 Mirror API(com.sun.mirror)来实现. Java 6 开始, 注解处理器被标准化, 定义在 JSR 269 标准中, 在标准库中提供了 API, apt 被集成到 javac 工具.

编译时 Java 6 注解处理器

了解注解处理器 API

注解处理器的 API 定义在javax.annotation.processing 包中, 其中 Processor 接口定义注解处理器, 子类 AbstractorProcessor 抽象类额外添加了便捷方法.

  • Processor 的实现类必须要有一个无参构造器
  • void init(ProcessingEnvironment processingEnv) 注解处理器初始化, 被调用的过程中传入的 ProcessingEnvironment 中包含了许多工具类:
  • boolean process(Set, RoundEnvironment) 注解处理方法, 定义注解处理
    • Java 编译器每遇到一个被 @SupportedAnnotationType 指定注解注解的类, 就调用一次 process
    • Set 当前注解处理器要处理的注解类型
    • RoundEnvironment 上一次注解处理中处理的源文件

注解处理器通过下面三个注解来进行配置:

  • @SupportedAnnotationType 指定注解处理器处理的注解类型, 该注解的元素为 String[] value, 需要传入注解类型全限定名, 也支持通配符
  • @SupportedSourceVersion 指定注解处理器处理的源代码版本, 该注解的元素为 SourceVersion value, 可选值定义在 javax.lang.model.SourceVersion 枚举类中
  • @SupportedOperation 指定注解处理器可以接收的命令行参数选项, 该注解的元素为 String[] value

定义注解

package gf.annotation;public @interface Complexity {    public enum Level {        VERY_SIMEPLE, SIMPLE, MEDIUM, COMPLEX, VERY_COMPLEX    } default @Complexity.MEDIUM}

注解类

pakcage gf.annotation;@Complexity(ComplexityLevel.VERY_SIMPLE)public class SimpleAnnotationTest {    @Complexity()    public void method() {        System.out.println("console");    }}

定义

@SupportedAnnotationTypes("gf.annotation.Complexity")@SupportedSourceVersion(SourceVersion.RELEASE_6)public class ComplexityProcessor exntends AbstractProcessor {    @Override    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {        for(Element elem: roundEnv.getElementsAnnotatedWith(Complexity.class)) {            Complexity complexity = elem.getAnnotation(Complexity.class);            String message = "annotated with: " + elem.getSimpleName();        }        return true;    }}

打包和注册

将注解处理器打包为 .jar 包, 需要在其中定义的 META-INF/services 目录下定义一个 javax.annotaiton.processing.Processor 文件, 内容为注解处理器的全限定名, 每行一个.

运行

将注解和注解处理器加入到 javac 的环境变量并进行注解处理

javac - cp MY_ANNOTATION.jar;MY_ANNOTATION_PROCESSOR.jar 被注解的源文件比如 javac -cp sdc.assets.annotations-1.0-SNAPSHOT.jar; sdc.assets.annotations.processors-1.0-SNAPSHOT.jar SimpleAnnotationsTest.java

Eclipse 对注解处理器的支持

项目右键 | Properties | Java Compiler | Annotation Processing 开启注解处理器 | Factory Path 指定包含注解处理器的 .jar 包位置, 每次项目构建时自动执行注解处理器。

Annotation Processing 界面配置注解处理器选项

  • 生成的源码目录, 默认位于项目根目录下的 .apt_generated
  • 处理器选项

Factory Path 界面指定使用的注解处理器的 .jar 包位置

关于注解的一些思考

(似乎什么是xx的问题本身就包含很多问题, xx的所有表现/存在共同定义了xx是什么. 然后从不同角度看待, 就会有不同的描述. 一个事物的没有绝对的意义, 需要通过外界来定义其自身. 所以在对xx进行定义时, 要明确其上下文和环境.)

注解是一种语言特性, 是一种解决方案. 每个解答都来自一个问题. 那么“问题1:是什么原因(原因是一个问题/不便)导致人们需要注解这样一种机制?”.

因为人们有着潜在的需要, “更好更快更方便…”. 在进行某个活动的过程中, 逐渐感受和意识到其中产生问题的地方和带来不便的地方. 进而就有想要改进的欲望. 而解决问题就是研究背后的原理和规律, 找出导致不便(”症状”)的原因(”病因”). 进而从量上进行改变, 增加或减少.

// 具体原因待查

总之, 要解决问题需要熟悉机理找出背后的原因, 促进某些因素, 抑制某些因素.

我们将注解定义为一种“更完善的描述程序的机制”, 那么“问题2:怎样理解‘完整’呢? ”

我认为, 通常先有了问题, 然后提出解决方案. 通常, 解决方案又促进了认识, 从而提出‘有了注解的程序才是完整的’. 也就是说在问题被解决前, 人们并不知道当时程序的描述是并不完整的. 实际上, 现在即使有了注解就一定能说我们现在对程序的描述是完整的了吗? 并不然, 只能相对没有注解之前而言, 是更加完整了. 现在如果有了新的需求/问题, 在解决方案提出之后, 我们可能又发现现在的有了注解的描述也可能是不完整的. 这就是计算机技术的发展规律, 在解决问题的过程中, 更好的方法被提出; 同时问题也在发生着改变. 这是一个不停演进的过程. “

也就是说, 事物是发展变化的, 完整是相对的. 解决方案也会使问题改变, 果和因也是相对的.

“问题3:现在我们是如何衡量其完整性的/什么样的描述对程序而言是完整的?”

// 待考究

一些认识

注解并不是Java编程语言的一部分. (正确. 在 Java SE 5 前是这样的)
注解是 Java 编程语言的一部分. (正确. 从 Java SE 5 开始是这样的)
注解这种机制是一种与 Java 编程语言无关的独立存在. (因为在 Java 引入该机制前它就存在了)
注解是一种让语言更好的存在, 而不是语言必须的部分. (因为在 Java 引入该机制前就能解决问题)

参考

  1. 《Java 编程思想(第4版)》
  2. Java 注解处理器
  3. 使用注解处理器生成代码 - 2 注解处理器
0 0