Annotation
简介
Annotation(注解)是JDK5开始引入的新特性,可以看作是一种特殊的注释,主要用于修饰类,方法或者变量,在框架中大量使用(如 Spring、Mybatis等)注解是一种能被添加到java代码中的元数据,类、方法、变量、参数和包都可以用注解来修饰。注解对于它所修饰的代码并没有直接的影响。
下面是简单的一个自定义注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Test {
public int id() default -1;
public String msg() default "Hi";
}
通过对上述文件的字节码(.class)的反编译(javap - p xxx.class)可以得到@interface其实就是一个继承了Annotation的一个接口
javap -p Test.class
public interface com.dyw.annotation.Test extends java.lang.annotation.Annotation {
public abstract int id();
public abstract java.lang.String msg();
}
注解只有被解析之后才会生效,常见的解析方法有两种:
编译期间直接扫描:编译器在编译Java代码的时候扫描对于的注解并处理,比如某个方法使用了@Override,编译器在编译的时候就会检测当前的方法是否重写了父类对于的方法。
运行期间通过反射处理:这个经常在Spring框架中看到,例如Spring的@Value注解,就是通过反射来进行处理的。
注解详细介绍
我们通过上述的例子可以看到我们的注解上面还有着其他的注解例如@Retention、@Target(这些都统称为元注解),所以一个注解是由以下成分组成
元注解
public @interface 注解名称
元注解介绍
JDK1.8版本为我们提供了6个标准的用来对注解类型进行注解的注解类(1.8之前只有四个),我们称之为meta-annotation(元注解)
元注解只能用在注解之上(自定义注解时可用)
@Target
@Retention
@Documented
@Inherited
@Native(1.8新增)
@Repeatable(1.8新增)
@Target
- 它指明了它所修饰的注解使用的范围 如果自定义的注解为含有@Target元注解修饰,那么默认可以是在(除类型参数之外的)任何项之上使用,若有@Target元注解修饰那么根据Value(ElementType枚举常量)的指定的目标进行规定。
public enum ElementType {
/** 类、接口(包括注解类型)或枚举声明 */
TYPE,
/** 字段声明(包括枚举常量) */
FIELD,
/** 方法声明 */
METHOD,
/** 参数声明 */
PARAMETER,
/** 构造函数声明 */
CONSTRUCTOR,
/** 局部变量声明 */
LOCAL_VARIABLE,
/** 注解类型声明 */
ANNOTATION_TYPE,
/** 包装声明 */
PACKAGE,
/**
* 类型参数声明 类型参数即Map<String,Integer>中的String和Integer这里是作为类型
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* 使用类型 对应于 JLS 4.11 中的 15 个类型上下文,以及两个声明上下文:类型声明(包括注解类型声明)和类型参数声明。
*
* @since 1.8
*/
TYPE_USE
}
ElementType的枚举常量指明了注解可以使用的目标。
@Target(ElementType.METHOD)//可修饰在方法之上
@Retention
- 即
@Retention
用来约束注解的生命周期,分别有三个值,源码级别(source)、类文件级别(class)或者运行时级别(runtime)可以通过指定@Retention
中的值来实现(值为RetentionPolicy
枚举常量)。 注意:生命周期大小排序为SOURCE < CLASS < RUNTIME,范围依次增大,前者能使用的地方后者一定能使用。如果需要在运行时去动态获取注解信息,那只能使用RUNTIME;如果要在编译时进行一些预处理操作,比如生成一些辅助代码,就是用CLASS;如果只是做一些检查性的操作,比如@Override和@SupperssWarning,可选择SOURCE
@Documented
- 带上该注解后的注解表明,在默认情况下这个注解是由JavaDoc和类似工具记录的,即带上了该文档化的注解被使用再生成文档时,会称为API的一部分。(默认情况下JavaDoc是不包含注解的,除非声明注解的时候使用了
@Documented
)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Person {
String name() default "";
}
public class main {
@Person(name = "ding")
public static void main(String[] args) {
System.out.println("hello");
}
//@Person(name = "ding")
@Person(name = "yang")
public static void doSome(){
}
}
生成的文档:
- 不带
@Documented
注解生成的文档
@Inherited
- 被该元注解修饰的自定义注解再使用后会自动继承,如果使用了该自定义注解去修饰一个class那么这个注解也会作用于该class的子类。就是说如果某个类使用了被
@Inherited
修饰的注解,则其子类将会自动具有该注释- 注意:
@Inherited annotation
类型是被标注过的class
的子类所继承。类并不从它所实现的接口继承annotation
,方法并不从它所重载的方法继承annotation
。
@Inherited //使用@Inherited修饰的自定义注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Person {
String name() default "";
}
public class main {
public static void main(String[] args) {
System.out.println(Father.class.getAnnotation(Person.class));
System.out.println(Son.class.getAnnotation(Person.class));
}
}
@Person
class Father {
}
class Son extends com.dyw.annotation.obj.Father {
}
@Native
- 使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native 注解不常使用,了解即可。
@Repeatable
@Repeatable
允许在相同的程序元素中重复注解(不报错)。在需要对同一种注解多次使用时,往往需要借助 @Repeatable 注解。Java 8 版本以前,同一个程序元素前最多只能有一个相同类型的注解,如果需要在同一个元素前使用多个相同类型的注解,则必须使用注解“容器”。
不使用@Repeatable
修饰的自定义注解完成重复注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Persons {
Person[] value();
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Person {
String name() default "";
}
@Persons(value = {@Person(name = "ding"),@Person(name = "yang")})
public static void doSome(){
}
使用@Repeatable
修饰的自定义注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Persons {
Person[] value();
}
@Repeatable(Persons.class)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Person {
String name() default "";
}
@Person(name = "ding")
@Person(name = "yang")
public static void doSome(){
}