全面解析Java注解

一、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,@Target,@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个方法可以调用
- public <T extends Annotation> T getAnnotation(Class<T> annotationClass) 按照传入的参数获取指定类型的注解。返回null说明当前元素不带有此注解。
- public final boolean isAnnotationPresent(Class<? extends Annotation> 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 Set<String> getSupportedAnnotationTypes()
    
    //指定该注解处理器使用的JAVA版本,通常返回SourceVersion.latestSupported()
    public SourceVersion getSupportedSourceVersion()
    
    //真正生成java代码的地方
    //annotations:请求处理的注解类型集合
    //roundEnv:可以让你查询出包含特定注解的被注解元素,相当于“有关全局源码的上下文环境”
    //如果返回 true,则这些注解已声明并且不要求后续 Processor 处理它们;
    //如果返回 false,则这些注解未声明并且可能要求后续 Processor 处理它们
    public abstract boolean process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv)
    
}

4.自定义注解处理器,就是继承AbstractProcessor并重写上述4个方法

关于编译时Annotation解析,这里推荐一篇文章【Android】注解框架(三)-- 编译时注解,手写ButterKnife,按照文章上面流程敲一遍代码,相信可以对自定义注解的创建及解析有一个深入的了解!

转载于:https://my.oschina.net/u/4006148/blog/2876629

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值