花了一天多的时间学习注解,下面是我的总结(编译时解析器还有点问题)
github地址:https://github.com/1711680493
注释,注解(Annotation)
也叫元数据,一种代码级别的说明,它是JDK1.5以上引入的一种特性,
与类,接口,枚举是在同一个层次,它可以声明在
(包,类,字段,方法,局部变量,方法参数)等,用来对这些元素进行说明,注释
在JDK中定义了很多注释,作用大致分为以下几类
编写文档:通过代码里标识的元数据生成文档[javadoc]
代码分析:通过代码里标识的元数据对代码进行分析[反射]
编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查(Override]
元注解:注解的注解(贴在注解上面的注解)
JDK内置的注解
@Override 限定覆写父类的方法
@Deprecated 标记已过时的成员,被标记的方法不推荐使用,使用在ide中会发出警告(黄色线条)
@SuppressWarnings 关闭不当编译器警告信息.
以下四种注解负责新注解的创建(元注解)
@Target 表示注解可以用于什么地方
@Target(ElementType.ANNOTATION_TYPE)
ElementType参数
TYPE; 类,接口,包括注解类型,或enum
FIELD; 域声明(包括enum实例)
METHOD; 方法声明
PARAMETER; 参数声明
CONSTRUCTOR; 构造器声明
LOCAL_VARIABLE; 局部变量声明
ANNOTATION_TYPE; 注解类型声明
PACKAGE; 包声明
JDK1.8中新增
TYPE_PARAMETER; 类型参数声明
TYPE_USE; 类似的使用
@Retention 表示该注解可以保存的范围
@Retention(RetentionPolicy.RUNTIME)
RetentionPolicy参数
SOURCE; 注释只能保存在源代码中,当编译就会被丢弃
CLASS; 主是可以在class中保留,但会被jvm丢弃
RUNTIME; 注释可以在运行时保留,可以通过反射获得
@Documented 拥有此注释的元素可以被 javadoc 的工具文档化
代表注解会被 javadoc 提取成文档,内容会因为注解的信息内容不同而不同
@Inherited 允许子类继承父类中的注解,拥有此注解的元素其子类可以继承父类的注解.
在Java中对应 java.lang.Annotation包
所有注解都继承自 Annotation 接口 The common interface extended by all annotation types
定义注解的方式
语法
public @interface 注解名 {元素体}使用 @interface 自定义注解时,自动继承了java.lang.annotation.Annotation接口
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Test {}
- 定义注解的时候需要用到元注解,
- 没有元素的注解称为 标记注解,上面的Test注解就是一个标记注解
- 注解可用类型包括(所有基本类型,String,Class,enum,Annotation,数组)
- 元素类似于接口的方法,不过可以设置默认值
- 元素必须初始化,不能使用null作为默认值
- 注解在只有一个元素且该元素的名称是value的情况下:
- 在使用注解的时候可以省略 value="" 直接写需要的值即可
例子:
public class TestAnnotation { @Test(name = "Shendi") public static void main(String[] args) { System.out.println("test"); } } @interface Test { public String name(); public String what() default "测试"; }
注解处理器(最重要的部分)
- 一个注解只是一种特殊的注释,如果没有 注解处理器,则可能连注释都不如.
- 解析一个类或者方法的注解往往有两种形式
- 编译期直接扫描
- 运行期反射
- 针对运行时注解会采用反射机制处理,针对编译时注解采用AbstractProcessor来处理
编译期直接扫描(使用AbstractProcessor)
典型的就是注解 @Override
javax.annotation.processing包
用于声明注解处理器和允许注解处理器与注解处理工具环境通信的设备AbstractProcessor抽象类 一个抽象的注解处理器,被设计为大多数具体注解处理器的一个方便的超类 继承此类来实现一个编译期的注解处理器 经常用到的方法 process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv); 对来自前一轮的类型元素处理一组注释类型,并返回此处理器是否声明这些注释类型. 等同于处理器的主函数,在这里写扫描,评估,处理代码... init(ProcessingEnvironment env); 每一个注解处理器类都必须有一个空的构造函数. 有一个特殊的init()方法会被注解处理工具调用,并输入ProcessingEnviroment参数 ProcessingEnviroment提供很多有用的工具类,Elements,Types和Filer getSupportedAnnotationTypes(); 指明注解处理器是处理那些注解的 必须指定,返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称. getSupportedSourceVersion(); 用来指定你使用的Java版本 通常这里返回SourceVersion.latestSupported(); 也可以返回其余的 例如Java6 SourceVersion.RELEASE_6
在Java7中,可以使用注解来代替getSupportedAnnotationTypes()
和getSupportedSourceVersion();
例如@SupportedSourceVersion(SourceVersion.latestSupported()) @SupportedAnnotationTypes({...}) class TestProcessor {}
建议使用重载,而不是注解,因为兼容的原因
必须知道的事情:注解处理器是运行在一个新的JVM中,javac启动一个完整的java虚拟机来运行注解处理器.
这意味着你可以使用任何你在其他Java应用中使用的东西注册你的处理器
- 在你的src下新建一个(文件夹) META-INF 下新建 services文件夹
- 在services文件夹中新建一个 javax.annotation.processing.Processor 文件
- 文件内容为注解处理器的全名 例如
- shendi.annotation.TestProcessor
- shendi.annotation.TestProcessor_Two
- ...后续完善,找到的大多都是Android的(貌似没有调用这个处理器),如果您知道这个怎么测试结果请回复
运行期注解(使用反射)
定义运行时注解如下
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface Test { String value(); }
使用注解
class UseAnnotation { @Test("Shendi") public String getName() { return null; } @Test("http://www.shendi.xyz") public String getIp() { return null; } }
注解处理器,获取注解的值,通过反射
public class TestAnnotation { public static void main(String[] args) { //获取此类所有的方法 判断是否有注释 Method[] methods = UseAnnotation.class.getDeclaredMethods(); //遍历方法 Arrays.asList(methods).forEach(method -> { System.out.println(method.getAnnotation(Test.class).value()); }); } }
输出如下:
Shendi
http://www.shendi.xyz