Java注解,新入门的菜鸟用的不多,但是想成长必须理清楚的一个知识点,今天来捋一捋,梳理一下。
首先,Java注解是干嘛的,我觉得注解是一种标记,或者说一种约束。比如在Android开发中使用的Retrofit框架就定义了几种注解,比如对请求方法的注解@GET @POST,
@GET("video/create.json")
Call<ResponseData<Video>> createVideo(@Query("title") String title,
@Query("description") String description,
@Query("videoUrl") String videoUrl,
);
这个createVideo()方法就被一个含有参数"video/create.json"的注解@GET标记,加了这个标记,Retrofit框架就知道这是一个get类型的请求,请求的路是"video/create.json"。所以这种注解就是一种标记。另外我们常见的
@Override
注解,实际上就是一种约束,表示被@Override标记的方法,必须继承自父类,是子类重写父类的方法。
注解既然是一种标记,所以它是一个静态的东西,不会发生主动行为。但凡注解起作用的场合都是有一个执行机制/检查者去查看这个标记然后根据它采取行动。对于刚才提到的@GET 注解,它的检查者就是Retrofit框架,@Override注解,检查者就是编译器。一般检查者都是通过反射来完成对注解的提取和识别判断并作出对应行动,这个下文再提。
现在我们知道了,注解就是对代码里某个东西的一种标记,那他都可以标记什么呢?Java中定义一个注解时使用@Target来表示该注解用于注解什么东西,一共有七种取值:
CONSTRUCTOR: 用于注解构造器
FIELD: 用于注解域
LOCAL_VARIABLE:用于注解局部变量
METHOD: 用于注解方法
PACKAGE: 用于注解包
PARAMETER: 用于注解参数
TYPE:用于注解类、接口(包括注解类型) 或enum声明
比如前边提到的@GET注解,@Target 的值就是METHOD,表明@GET注解用于注解一个方法。
@Documented
@Target(METHOD)//这里这里这里
@Retention(RUNTIME)
public @interface GET {
String value() default "";
}
定义一个注解时使用@Target表示该注解的使用场景,那很明显应该还有其他符号用来对这个注解进行描述。这里其实@Target是注解的形式,所以可以说是注解的注解,也叫做元注解,Java中一共定义了四种元注解:
1. @Documented —— 指明拥有这个注解的元素可以被javadoc此类的工具文档化。javadoc工具可以根据该注解包含的信息内容不同而生成对应的注释文档。
2. @Target——指明该注解可以注解的程序元素的范围。取值是上边提到的七种。如果Target元注解没有出现,那么定义的注解可以应用于程序的任何元素。
3. @Inherited——指明该注解类型被自动继承。检查被定义注解的时候使用,如果用户在当前类中查询这个元注解类型并且当前类的声明中不包含这个元注解类型,那么也将自动查询当前类的父类是否存在Inherited元注解,这个动作将被重复执行知道这个标注类型被找到,或者是查询到顶层的父类。
4.@Retention——指明了该Annotation被保留的时间长短。RetentionPolicy取值为SOURCE,CLASS,RUNTIME。三个取值分别对应
SOURCE----> 源码注解:只在源码中存在,编译成.class文件就不存在了。
CLASS-------> 编译时注解:在源码和.class文件中都存在。像@Override、@Deprecated都属于编译时注解。
RUNTIME---> 运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解。
有了这个四个元注解,就可以用他们几个随意组合起来去定义一个注解了。像这样:
@Target(METHOD)
public @interface MyAnnotation {
...
}
MyAnnotion就是一个可以使用的注解,@Target设成了METHOD,用@MyAnnotion放在要注解的方法上就好了。
@MyAnnotation
public void method() {
.....
}
这里的@MyAnnotion是一个空的注解,实际上注解类中可以定义定义一些元素,称为注解元素,就像@GET方法中String value() default "";
@Documented
@Target(METHOD)//这里这里这里
@Retention(RUNTIME)
public @interface GET {
String value() default "";
}
使用时@GET(value = “video/create.json”),当注解只有一个String元素时,尽量定义成value,这样使用注解时就可以省略直接写成@GET("video/create.json"),这样注解的检查起会自动将 “video/create.json”赋给value。
注解的元素在定义时,也有一些限制:
注解方法不能带有参数;
注解方法返回值类型限定为:基本类型、String、Enums、Annotation或者是这些类型的数组;
注解方法一般要给出默认值;
注解本身能够包含元注解。
例如可以定义一个注解@MethodInfo:
@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodInfo{
String author() default 'Pankaj';
String date();
int revision() default 1;
String comments();
}
@MethodInfo在使用时可以给author丶date丶revision丶comments四个元素分别赋值,这样在注解的检查器检查时就可以拿到对应的值。
注解检查器,注解检查器通过反射的方式获取注解使用时标记的信息,然后根据信息作出不同的应对。
例如定义一个@MyAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.METHOD, ElementType.TYPE })//既可以注解类,也可以注解类
public @interface MyAnnotation {
String color() default "blue";//为属性指定缺省值
String value();//定义一个名称为value的属性
}
可以用@MyAnnotation 注解一个类,并在该类执行的时候,通过反射获取注解的信息:
@MyAnnotation("apple")// 等价于@MyAnnotation(value="apple")
public class MyAnnotationTest {
public static void main(String[] args) {
/**
* 用反射方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法
*/
MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(annotation.color());// 输出color属性的默认值:blue
System.out.println(annotation.value());// 输出apple
}
}
实际上,由于@MyAnnotation 注解定义了可以注解一个方法,我们可以在MyAnnotationTest的某个方法上使用@MyAnnotation 注解,同样的利用反射的机制,读取注解的信息:
for(Field filed : MyAnnotationTest.class.getDeclaredFields()){
String columnName = null;
Annotation[] anns = filed.getAnnotations();// 得到属性的注解,对一个目标可以使用多个注解
}
拿到方法的注解数组之后可以遍历数组,判断注解的类型:
if(anns[0] instanceof MyAnnotation){
//是@MyAnnotation类型的注解,可以调用anns[0].color()和anns[0].value()
}
参考链接,鸣谢:
http://www.importnew.com/17413.html
http://www.cnblogs.com/xdp-gacl/p/3622275.html
http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html