一、Java注解基础知识点
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特写,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
作用分类
1.编写文档:通过代码里标识的元数据生成文档【生成doc文档】
2.代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
3.编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查
- 注解是一种可以添加到Java源代码的元数据
- 类、方法、变量、参数、包都可以被注解
- 注解对注解的代码并没有直接的影响
- 注解仅仅是个标记,注解之所以起作用是对其解析后做了相应的处理
二、Annotation分类
1.标准Annotation
- 标准Annotation是指Java内置的三个Annotation
- @Override:用于修饰此方法覆盖了父类的方法
- @Deprecated:用于修饰已经过时的方法
- @SuppressWarnnings:用于通知Java编译器禁止特定的编译警告
2.元Annotation(注解的注解)
- 元Annotation是用来定义Annotation的Annotation
- 元Annotation可以定义Annotation的作用范围,使用在什么元素上等
- 元注解共有四种@Retention,,@lnherited,@Documented
3.自定义Annotation
三、元Annotation
1.@Retention:注在其他的注解A上,用来说明A的保留范围,可选值 SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASS
- SOURCE:A只保留在源码中,A会被编译期忽略.(源码可用)
- CLASS:A会通过编译保存在CLASS文件中,但会被JVM在运行时忽略,运行时不可见.(源码+CLASS可用)
- RUNTIME:A会被JVM获取,并在运行时通过反射获取.(源码+CLASS+运行时均可用)
2.@Target:注在其他的注解A上,用来限制A可用修饰那些程序元素.未标注Target表示无限制,可修饰所有元素.
- ANNOTATION_TYPE: A可以应用到其他注解上
- CONSTRUCTOR: A可以使用到构造器上
- FIELD: A可以使用到域或属性上
- LOCAL_VARIABLE: A可以使用到局部变量上。
- METHOD: A可以使用到方法上。
- PACKAGE: A可以使用到包声明上。
- PARAMETER: A可以使用到方法的参数上
- TYPE: A可以使用到类,接口(包括注解),或枚举的声明上
3.@Inherited:默认情况下,父类的注解不会被子类继承.
- Inherited注在其他的注解A上.
- 只有当A是注解在类Class上面,Inherited才会起作用,其他任何情况下无效果.
- 当A注解在类C上面,则C的所有子孙类,都会继承应用A注解;
4.@Documented:注在其他的注解A上,A将会作为Javadoc产生的文档中的内容。注解都默认不会成为成为文档中的内容。
四、自定义Annotation
1.创建自定义Annotation流程
- public @interface 自定义注解名称
public @interface CustomAnnotation{***}
- 设置自定义Annotation的保留范围和目标,Retention和Target是最重要的两个元Anitation.
@Retention( RetentionPolicy.RUNTIME )@Target( ElementType.TYPE )public @interface CustomAnnotation{***}
- 设置自定义Annotation的注解参数(注解成员)
- 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
- String类型
- Class类型
- enum类型
- Annotation类型
- 以上所有类型的一维数组
- 注解参数声明方式
@Retention( RetentionPolicy.RUNTIME )@Target( ElementType.TYPE )public @interface CustomAnnotation{ //注解参数类型可以是1-6中任一种,包括枚举 public enum Skill{JAVA,ANDROID,IOS} Skill mySkill() default Skill.ANDROID; String attr1(); //可以使用default设置默认值 int attr2() default 100; //修饰符只能用public public boolean attr3() default false;}@Retention( RetentionPolicy.RUNTIME )@Target( ElementType.TYPE )public @interface CustomAnnotation{ //只有一个注解参数,使用value() String value();}
- 自定义Annotation的参数类型必须满足上一条1到6中的范围.
- 自定义Annotation的参数访问方法只能是public,或不写.
- 自定义Annotation的参数可以加 default 设置默认值.
- 自定义Annotation若只有1个参数,使用value().
2.自定义Annotation的注解参数的默认值
注解元素必须有确定的值,要么定义在注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。因此,使用空字符串或0作为默认值是一种常用的做法。这个约束使得处理器很难表现一个元素的存在或缺失的状态,因此每个注解的声明中,所有元素都存在,并且都具有相应的值,为了绕开这个约束,我们只能定义一些特殊的值,例如空字符串或者负数,一次表示某个元素不存在,在定义注解时,这已经成为一个习惯用法。
示例:@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface AnotherAnnotation{ String author() default ""; int age() default -1;}
3.使用刚刚创建的自定义注解
@CustomAnnotation(attr1 = "属性1", attr2 = 90, attr3 = true)public class AnnotationTestClass{ ***}
五、Annotation解析
- 运行时 Annotation 解析
运行时 Annotation 指 @Retention 为 RUNTIME 的 Annotation- Class,Method,Field中都有以下3个方法可以调用- publicT getAnnotation(Class annotationClass) 按照传入的参数获取指定类型的注解。返回null说明当前元素不带有此注解。- public final boolean isAnnotationPresent(Class annotationType) 检查传入的注解是否存在于当前元素。- public Annotation[] getAnnotations() 返回该元素的所有注解,包括没有显式定义该元素上的注解。- 运行时 Annotation 解析示例public void testCustomAnnotation() { try { Class cls = Class.forName("com.jet.annotation.AnnotationTestClass"); CustomAnnotation customAnnotation = (CustomAnnotation)cls.getAnnotation(CustomAnnotation.class); System.out.println("customAnnotation mySkill:" + cus.mySkill()); System.out.println("customAnnotation attr1:" + cus.attr1()); System.out.println("customAnnotation attr2:" + cus.attr2()); } catch (ClassNotFoundException e) { e.printStackTrace(); }}
- 编译时 Annotation 解析
编译时 Annotation 指 @Retention 为 CLASS 的 Annotation,甴编译器自动解析
六、编译时Annotation解析
编译时Annotation解析 相对复杂,下面单独进行分析
首先申明:下面内容仅仅讨论 编译时Annotation的解析
1.编译时Annotation的解析,是由Annotation Processor完成
2.Annotation Processor(注解处理器)
- 注解处理器是一个在javac中的,用来在编译时扫描和处理注解的工具
- 我们可以为特定的注解,注册自定义的注解处理器
- 在编译期间,JVM会自动运行注册过的注解处理器
- 一个注解的Annotation Processor,以Java代码(或者编译过的class)为输入,生成.java文件作为输出.这意味着我们可以生成新的Java代码!这些生成的Java代码是在生成的.java文件中,新生成的.java文件会和普通的手动编写的Java源代码一样被javac编译
3.每一个注解处理器都是继承于AbstractProcessor,需要关注的有以下4个方法
public abstract class AbstractProcessor implements Processor { //对一些工具进行初始化 public synchronized void init(ProcessingEnvironment processingEnv) //你在这里定义你的注解处理器注册到哪些注解上,必须指定; //它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称 public SetgetSupportedAnnotationTypes() //指定该注解处理器使用的JAVA版本,通常返回SourceVersion.latestSupported() public SourceVersion getSupportedSourceVersion() //真正生成java代码的地方 //annotations:请求处理的注解类型集合 //roundEnv:可以让你查询出包含特定注解的被注解元素,相当于“有关全局源码的上下文环境” //如果返回 true,则这些注解已声明并且不要求后续 Processor 处理它们; //如果返回 false,则这些注解未声明并且可能要求后续 Processor 处理它们 public abstract boolean process(Set annotations,RoundEnvironment roundEnv) }
4.自定义注解处理器,就是继承AbstractProcessor并重写上述4个方法
关于编译时Annotation解析,这里推荐一篇文章,按照文章上面流程敲一遍代码,相信可以对自定义注解的创建及解析有一个深入的了解!