Java 注解其实大家不陌生,使用 spring、mybatis 这样的框架时,会有许多的注解,那注解到底是什么?
java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用
注解 (annotation) 是在实际的源代码级别保存所有的信息,而不是某种注释性的文字 (comment)
常见注解
Java 注解是从 Java 5 引入的,也称作 元数据
,即描述数据的数据
Java SE5 内置了三种标准注解:
@Override
,表示当前的方法定义将覆盖超类中的方法。拼写错误或方法签名对不上覆盖的方法,编译器会错误提示@Deprecated
,表示过时的方法,表示被弃用的代码,使用了注解为它的元素编译器将发出警告@SuppressWarnings
,关闭不当编译器警告信息。
基本语法
要学习注解,我们就必须要能定义自己的注解,并使用注解,在定义自己的注解之前,我们就必须要了解Java为我们提供的元注解和相关定义注解的语法。
定义注解格式: public @interface 注解名 {定义体}
定义注解
1 | (ElementType.METHOD) |
注解的定义看起来很像接口的定义,事实上,注解也会被编译成 class 文件
定义注解时,会需要一些 元注解(meta-annotation),如 @Target 和 @Retention。在注解中,一般会有一些元素(参数)以表示某些值,分析处理注解时,可以去利用这些值。没有元素的注解为 标记注解(marker annotation)
使用注解
1 | public class TestExample{ |
被注解的方法和其他方法没有区别,如上,注解@Test 可以和修饰符共同作用于方法,从语法角度,注解的使用方式与修饰符一样
元注解
元注解是用来注解其他的注解,也就是我们自定义注解需要元注解
@Target
描述该注解的使用范围,可能的ElementType参数有:
CONSTRUCTOR
:构造器的声明FIELD
:域声明(包括enum
实例)LOCAL_VARIABLE
:局部变量声明METHOD
:方法声明PACKAGE
:包声明PARAMETER
:参数声明TYPE
:类、接口(包括注解类型)或enum
声明 |
@Retention
表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:
SOURCE
:注解将被编译器丢弃CLASS
:注解在class文件中可用,但会被VM丢弃RUNTIME
:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息 |
@Documented
将注解包含在Javadoc中
@Inherited
允许子类继承父类中的注解 |
注解元素
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。
注解中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
注解类型里面的元素该怎么设定:
只能用 public 或默认(default)这两个访问权修饰
只能用基本类型(byte,short,char,int,long,float,double,boolean)八种基本数据类型和 String, Enum, Class, Annotation 等数据类型,以及这些类型的数组
如果只有一个参数成员,最好把参数名称设为 value(注解在只有一个元素且该元素的名称是value的情况下,在使用注解的时候可以省略“value=”,直接写需要的值)
默认值限制
注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null
1 | (ElementType.FIELD) |
1 | (ElementType.FIELD) |
1 | public class Apple { |
注解处理器
注解的本质其实是一个继承了 java.lang.annotation.Annotation 接口的接口
The common interface extended by all annotation types
如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了。使用注解的过程中,很重要的一部分就是创建和使用注解处理器。Java5扩展了反射机制的API,以帮助程序员快速的构造自定义注解处理器。
注解处理器类库(java.lang.reflect.AnnotatedElement):
Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素,该接口主要有如下几个实现类:
- Class:类定义
- Constructor:构造器定义
- Field:累的成员变量定义
- Method:类的方法定义
- Package:类的包定义
当一个Annotation类型被定义为运行时的Annotation后,该注解才能是运行时可见,可以使用反射获取Annotation 信息。AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个个方法来访问Annotation信息:
方法1: T getAnnotation(Class annotationClass):返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
方法2:Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
方法3:boolean isAnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注解。(如果没有注解直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。
1 | public class FruitInfoUtil { |