注解(annotation)是Java 5 引入的,用来为类、方法、字段、参数等Java结构提供额外信息的机制. 比如Java核心类库中的@Override注解是被用来声明某个实例方法重写了父类的同名同参数类型的方法.
注解是允许自定义的,比如用来生成Java doc、自动代码生成等等.
元注解就是注解上的注解,有下面5种
- @Retention
- @Target
- @Inherited
- @Documented
- @Repeatable
定义该注解的生命周期
- RetentionPoicy.SOURCE 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
- RetentionPolicy.CLASS 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式
- RetentionPolicy.RUNTIME 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
表示该注解用于什么地方。如果不明确指出,该注解可以放在任何地方
ElementType.TYPE:用于描述类、接口或enum声明
ElementType.FIELD:用于描述实例变量
ElementType.METHOD 方法
ElementType.PARAMETER
ElementType.CONSTRUCTOR
ElementType.LOCAL_VARIABLE
ElementType.ANNOTATION_TYPE 另一个注释
ElementType.PACKAGE 用于记录java文件的package信息
是否允许子类继承父类的注解,默认是 false。
是否会保存到 Javadoc 文档中。
JDK1.8新加的,表明当自定义注解的属性值有多个时,自定义注解可以多次使用。
自定义注解根据有无成员变量分为标记型注解和元数据注解.定义了注解之后,还需要解析注解才有实际作用.解析注解也分为2种,它是根据Retention来划分的,如果是编译时注解,可以采用APT(编译期的注解处理器,开发人员自己写)来解析;如果是运行时注解,则采用反射来获取.
下面是个简单的例子获取运行时注解
private void getMethodsAnnotation() {
Class<AnnotationTest> annotationTest = AnnotationTest.class;
//获取所有方法
for (Method method : annotationTest.getMethods()) {
//获取该方法上面的注解
MyTodo annotation = method.getAnnotation(MyTodo.class);
if (annotation != null) {
//获取方法名
System.out.println("Method name : " + method.getName());
//获取注解的值
System.out.println("Author : " + annotation.author());
System.out.println("Priority : " + annotation.priority());
System.out.println("Status : " + annotation.status());
}
}
}
再来说说编译期注解,编译期注解的解析需要通过APT来实现.可以继承AbstractProcessor来自定义注解处理逻辑,但是还得需要向编译器注册注解处理器.一般需要依赖Google的AutoService库来配合.
编译期注解运行原理:
Java源代码的编译流程分为3个步骤:
- 将源文件解析成抽象语法树
- 调用已注册的注解处理器
- 生成字节码
如果第二步调用注解处理器过程中生成了新的源文件,那么编译期将重复第1,2步骤,解析并处理新生成的源文件.
- 资源类型注解: @AnimRes、@ColorRes、@LayoutRes - 空值注解: @Nullable、@NonNull
- 值约束注解: @IntRange、@FloatRange
- 权限注解: @RequiresPermission
- 线程注解: @MainThread、@UIThread、@WorkerThread
- Java中的常用注解: @Override、@Deprecated、@SuppressWarnings