Java注解
1.注解是什么,有什么意义
注解本身没有任何意义,单独的注解就是一种注释,他需要结合其他如反射、插桩等技术才有意义。
Java 注解(Annotation)又称 Java 标注,是 JDK1.5 引入的一种注释机制。是元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。
2.注解的定义
注解的定义很简单,我们直接使用@interface关键字就可以定义一个注解。
public @interface Test {}
这只是注解的简单定义,这么写是没有任何意义的,我们还需要加入其它的配置才可以。我们往下看。
3.元注解
在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为 meta-annotation(元注解)。
@Target
注解标记另一个注解,以限制可以应用注解的 Java 元素类型。目标注解指定以下元素类型之一作为其值:
- @Target(ElementType.TYPE) 作用接口、类、枚举、注解
- @Target(ElementType.FIELD) 作用属性字段、枚举的常量
- @Target(ElementType.METHOD) 作用方法
- @Target(ElementType.PARAMETER) 作用方法参数
- @Target(ElementType.CONSTRUCTOR) 作用构造函数
- @Target(ElementType.LOCAL_VARIABLE)作用局部变量
- @Target(ElementType.ANNOTATION_TYPE)作用于注解(@Retention注解中就使用该属性)
- @Target(ElementType.PACKAGE) 作用于包
- @Target(ElementType.TYPE_PARAMETER) 作用于类型泛型,即泛型方法、泛型类、泛型接口 (jdk1.8加入)
- @Target(ElementType.TYPE_USE) 类型使用.可以用于标注任意类型除了 class (jdk1.8加入)
下面我们看下Target的源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
由上面的源码可知Target可以指定多个,因为它的值是数组。
@Retention
注解指定标记注解的存储方式:
- @Retention(RetentionPolicy.SOURCE),标记的注解仅保留在源级别中,并被编译器忽略。
- @Retention(RetentionPolicy.CLASS), 标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略。
- @Retention(RetentionPolicy.RUNTIME), 标记的注解由 JVM 保留,因此运行时环境可以使用它。
使用场景
- RetentionPolicy.SOURCE:在编译期能够获取注解与注解声明的类包括类中所有成员信息,一般用于生成额外的辅助类。典型的代表,APT(Annotation Processor Tools 注解处理工具) 。
注解处理器运行在编译阶段:java文件经过javac编译成.class文件,
javac采集到所有的注解信息,包装成一个节点(Element),再由javac调用指定的注解处理程序。javac编译java文件的时候会调用注解处理器,然后在编译为class文件。所以注解处理器使用源码级别的注解就可以了。
还有就是编译器代码检查,例如IntDef、@DrawableRes,语法检查是由ide或者ide插件实现的。
-
RetentionPolicy.CLASS:
在编译出Class后,通过修改Class数据以实现修改代码逻辑目的。对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解。
字节码增强:在字节码中写代码 -
Retention(RetentionPolicy.RUNTIME)
在程序运行期间,通过反射技术动态获取注解与其元素,从而完成不同的逻辑判定。
@Inherited
一个被@Inherited注解了的注解修饰了一个父类,如果他的子类没有被其他注解修饰,则它的子类也继承了父类的注解。
/**
* 自定义注解
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyTestAnnotation {
}
/**
* 父类标注自定义注解
*/
@MyTestAnnotation
public class Father {}
//子类
class Son extends Father{}
public static void main(String[] args) {
//获取Son的class对象
Class<Son> sonClass = Son.class;
// 获取Son类上的注解MyTestAnnotation可以执行成功
boolean annotation = sonClass.isAnnotationPresent(MyTestAnnotation.class);
//打印值为true
System.out.println(annotation);
}
@ Repeatable
被这个元注解修饰的注解可以同时作用一个对象多次
//Persons
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Persons {
Person[] value();
}
//Person注解
@Repeatable(Persons.class)
public @interface Person{
String role() default "";
}
@Person(role="shuaige")
@Person(role="shuaibi")
@Person(role="dashuaige")
@Person(role="dashuaibi")
public class ShuaiGe {
String name="";
}
public static void main(String[] args) {
if (ShuaiGe.class.isAnnotationPresent(Persons.class)) {
Persons p2 = Man.class.getAnnotation(Persons.class);
for (Person t : p2.value()) {
System.out.println(t.role());
}
}
}
@Documented
用于被javadoc工具提取成文档。
4.注解类型元素
在上文元注解中,允许在使用注解时传递参数。我们也能让自定义注解的主体包含 annotation type element (注解 类型元素) 声明,它们看起来很像方法,可以定义可选的默认值。
@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface ShuaiGe {
String value(); //无默认值
int age() default 1; //有默认值
}
注意:在使用注解时,如果定义的注解中的类型元素无默认值,则必须进行传值。
@Lance("帅") //如果只存在value元素需要传值的情况,则可以省略:元素名=
@Lance(value="帅",age = 2)
int i;