在写代码的时候,总会在类名或者方法名的上面出现@xxx这样的语法,开始的时候我是不会用这种注解符号的,只明白一些注解是起说明作用的如@Overwrite这种,后来接触了Spring之后才明白,这种注解其实有比较大的用处,结合一些博文和自己的实际应用,记录下自己使用的注解和框架中应用注解的实现机制。
1.java中注解的基本使用方式和声明
(1)元注解
1.@Target,
2.@Retention,
3.@Documented,
4.@Inherited
以上几个元注解是为真正我们要实现的注解服务的。
1.@Target 此字段是用于描述注解可以被应用的范围,可被使用的范围有 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)
可以取的值有:
1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
Target可以被添加到目标注解的头部,表示目标注解的应用范围。
2.@Retention定义了该Annotation被保留的时间长短:简单来说就是目标注解的作用范围;
可以取的值为:
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)
一般都是用于RUNTIME
3.@Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API
用于声明当前修饰的注解能不能生成在java doc中,此参数是没有成员的。
4.@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
此元注解我用到的不多,就不多说了
(2).自定义注解的实现
自定义注解其实是基于元注解去实现的一种注解,通常主要用于拦截器或者成员的声明等,使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
下面自定义实现一个java的Annotation:
首先定义一个Annotation:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)//声明的注解要加到字段是上
@Retention(RetentionPolicy.RUNTIME)//声明为运行时要使用的
public @interface ColorAnnotation {
public String value() default "red";//默认值
}
定义一个使用此注解的类:
public class ColorTest {
@ColorAnnotation("yellow")
public String color;
}
只定义和使用注解是远远达不到目的的,接下来我们还需要对注解进行解析
Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素,该接口主要有如下几个实现类:
Class:类定义
Constructor:构造器定义
Field:累的成员变量定义
Method:类的方法定义
Package:类的包定义
java.lang.reflect 包下主要包含一些实现反射功能的工具类,实际上,java.lang.reflect 包所有提供的反射API扩充了读取运行时Annotation信息的能力。当一个Annotation类型被定义为运行时的Annotation后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个个方法来访问Annotation信息:
方法1:<T extends Annotation> T getAnnotation(Class<T> annotationClass): 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
方法2:Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。
接下来是对注解进行解析:
import java.lang.reflect.Field;
public class ColorPaser {
public static void parse(Class<?> clazz){
Field fields[] = clazz.getDeclaredFields();
for(Field f : fields){
if(f.isAnnotationPresent(ColorAnnotation.class)){
ColorAnnotation color = f.getAnnotation(ColorAnnotation.class);
String colorStr = color.value();
System.out.println(colorStr);
}
}
}
public static void main(String args[]){
ColorPaser.parse(ColorTest.class);
}
}
这样就实现了对注解的解析处理。当然实际应用中这种注解的解析模式可以应用到拦截器上面,我们现在用的框架就是自己定义实现了一个通过注解进行的拦截器。然后在Filter中进行解析请求 映射Controller解析到对应的注解进行对请求的拦截。