Java注解

 

摘抄并学习

 

  以前,【XML】是各大框架的青睐者,它以松耦合的方式完成了框架中几乎所有的配置,但是随着项目越来越大,XML 的内容也越来越复杂,维护成本变高。

  于是,有人提出一种标记式高耦合的配置方式,【注解】。方法上可以进行注解,类上也可以注解,字段属性也可以注解,反正几乎需要配置的地方都可以进行注解。

  关于【注解】和【XML】两种不同的配置模式,争论了好多年,各有各的优劣,注解可以提供更大的便捷性,易于维护修改,但耦合度高,而 XML 相对于注解则是相反的。

  追求低耦合就要抛弃高效率,追求效率必然会遇到耦合。

 

 

注解的本质

  接口中有这么一句话,用来描述注解,“所有的注解类型都继承自这个普通的接口(Annotation)

 

  一个注解准确意义上来说,只不过是一种特殊的注释,如果没有解析它的代码,它可能连注释都不如

 

  而解析一个类或者方法的注解往往有两种形式,一种是编译期直接的扫描,一种是运行期反射。反射的事情我们待会说,而编译期的扫描指的是编译器在对 java 代码编译字节码的过程中会检测到某个类或者方法被一些注释修饰,这时它就会对于这些注解进行某些处理。

  典型的就是注解 @Override,一旦编译器检测到某个方法被修饰了 @Override 注解,编译器就会检查当前方法的签名是否真正重写了父类的某个方法,也就是比较父类中是否具有一个同样的方法签名。

  这一种情况只适用于那些编译器已经熟知的注解类,比如 JDK 内置的几个注解,而你自定义的注解,编译器是不知道你这个注解的作用的,当然不知道怎么处理,往往只是会根据该注解的作用范围来选择是否编译进字节码文件。

 

 

元注解

  【元注解】是用于修饰注解的注解,通常用在注解的定义上,例如:

  @Target(ElementType.METHOD)

  @Retention(RetentionPolicy.SOURCE)

  public @interface Override {

  }

  这是我们 @Override 注解的定义,你可以看到其中的 @Target,@Retention 两个注解就是我们所谓的 元注解,元注解 一般用于指定某个注解生命周期以及作用目标等信息。

JAVA中有以下几个【元注解】:

  • @Target:注解的作用目标
  • @Retention:注解的生命周期
  • @Documented:注解是否应当被包含在 JavaDoc 文档中
  • @Inherited:是否允许子类继承该注解

 

  其中,@Target 用于指明被修饰的注解最终可以作用的目标是谁,也就是指明,你的注解到底是用来修饰方法的?修饰类的?还是用来修饰字段属性的。

  @Target(value = {ElementType.FIELD})

  被这个 @Target 注解修饰的注解将只能作用在成员字段上,不能用于修饰方法或者类。其中,ElementType 是一个枚举类型,有以下一些值:

  • ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上
  • ElementType.FIELD:允许作用在属性字段上
  • ElementType.METHOD:允许作用在方法上
  • ElementType.PARAMETER:允许作用在方法参数上
  • ElementType.CONSTRUCTOR:允许作用在方法参数上
  • ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
  • ElementType.ANNOTATION_TYPE:允许作用在注解上
  • ElementType.PACKAGE:允许作用在包上

 

  @Retention 用于指明当前注解的生命周期

  

  @Documented

  @Retention(RetentionPolicy.RUNTIME)

  @Target(ElementType.ANNOTATION_TYPE)

  public @interface Retention {

    RetentionPolicy value();

  }

  同样的,它也有一个 value 属性

  @Retention(value = RetentionPolicy.RUNTIME)

  这里的 RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件

  • RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件
  • RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件
  • RetentionPolicy.RUNTIME:永久保存,可以反射获取

 

  @Retention 注解指定了被修饰的注解的生命周期,一种是只能在编译期可见,编译后会被丢弃,一种会被编译期编译进 class 文件中,无论是类或者方法,和字段,他们都是有属性表的,而 JAVA 虚拟机也定义了几种注解属性表用于存储注解信息,但是这种可见性不能带到方法区,类加载时会丢弃,最后一种则是永久存在的可见性。

 

  @Documented 注解修饰的注解,当我们执行 JavaDoc 文档打包时会被保存进 doc 文档,反之将在打包时丢弃。

 

  @Inherited 注解修饰的注解是具有可继承性的,也就说我们的注解修饰了一个类,而该类的子类将自动集成父类的该注解。

 

JAVA 的内置三大注解

  除了上述四种元注解外,JDK 还为我们预定义了另外三种注解,他们是:

  • @Override
  • @Deprecated
  • @SuppressWarnings

 

  @Override 注解没有任何属性,所以并不能存储任何其他信息。它只能作用于方法上,编译结束后将被丢弃。

  他是一种典型的 “标记式注解” ,仅被编译器可知,编译器在对 java 文件进行编译成字节码的过程中,一旦检测到某个方法上被修饰了该注解,就会去匹对父类中是否具有一个同样方法签名的函数,如果不是,就不能通过编译。

 

  @Deprecated 依然是一种 “标记式注解” ,永久存在,可以修饰所有的类型,作用是,标记当前的类或者方法或者字段等已经不再被推荐使用了,可能下一次的 JDK 版本就会删除。

  当然,编译器并不会强制要求你做什么,只是告诉你 JDK 已经不再推荐使用当前的方法或者类了,建议你使用某个替代者。

 

  @SuppressWarnings 主要用来压制 java 的警告,它有一个 value 属性需要你主动的传值,这个 value 代表就是需要被压制的警告类型。

 

 

注解与反射

  

  虚拟机规范定义了一系列和注解相关的属性表,也就说,无论是字段、方法或是类本身,如果被注解修饰了,就可以被写进字节码文件。属性表有以下几种:

  1. RuntimeVisibleAnnotations:运行时可见的注解
  2. RuntimeInVisibleAnnotations:运行时不可见的注解
  3. RuntimeVisibleParameterAnnotations:运行时可见的方法参数注解
  4. RuntimeInvisibleParameterAnnotations:运行时不可见的方法参数注解
  5. AnnotationDefault:注解类元素的默认值

  

  所以,对于一个类或者接口来说,Class 类中提供了以下一些方法用于反射注解。

  1. getAnnotation:返回指定的注解
  2. isAnnotationPresent:判定当前元素是否被指定注解修饰
  3. getAnnotations:返回所有的注解
  4. getDeclaredAnnotation:返回本元素的指定注解
  5. getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的

  方法、字段中相关反射注解的方法基本是类似的。

 

  注解的本质是继承了 Annotation 接口的接口,而当你通过反射,也就是 getAnnotation 方法获取一个注解类实例的时候,其实 JDK 是通过动态代理机制生成一个实现我们注解的代理类。

 

 

 

https://www.cnblogs.com/yangming1996/p/9295168.html

 

posted @ 2019-10-29 11:10  停不下的时光  阅读(207)  评论(0编辑  收藏  举报