Annotation注解

基本概念

三种内置注解:

  1. @Override表示当前的方法定义将覆盖超类中的方法。

  2. @Deprecated使用了注解为它的元素编译器将发出警告,因为注解@Deprecated是不赞成使用的代码,被弃用的代码。

  3. @SuppressWarnings关闭不当编辑器警告信息。

元注解

定义注解的注解,是java提供给我们用于定义注解的基本注解.在java.lang.annotation

@Target

用于定义注解的作用目标,表示该注解可以用于什么地方,ElementType参数有:

1
2
3
4
5
6
7
8
@Target(ElementType.TYPE)           用于接口(注解本质上也是接口),类,枚举
@Target(ElementType.FIELD) 用于字段,枚举常量
@Target(ElementType.METHOD) 用于方法
@Target(ElementType.PARAMETER) 用于方法参数
@Target(ElementType.CONSTRUCTOR) 用于构造参数
@Target(ElementType.LOCAL_VARIABLE) 用于局部变量
@Target(ElementType.ANNOTATION_TYPE)用于注解
@Target(ElementType.PACKAGE) 用于包
@Retention

用于定义注解保留策略,即定义的注解类在什么时候存在(源码阶段 or 编译后 or 运行阶段).

1
2
3
@Retention(RetentionPolicy.SOURCE)	注解仅在源码中保留,class文件中不存在
@Retention(RetentionPolicy.CLASS) 注解在源码和class文件中都存在,但运行时不存在,即运行时无法获得,该策略也是默认的保留策略
@Retention(RetentionPolicy.RUNTIME) 注解在源码,class文件中存在且运行时可以通过反射机制获取到
@Documented

用于描述其它类型的annotation应该被javadoc文档化,出现在api doc中

@Inherited

允许子类继承父类中的注解

自定义注解

例如:

1
2
3
@Target({ElementType.TYPE})   ---作用范围 Class
@Retention(RetentionPolicy.CLASS) ---生命周期:仅保留到.class文件
public @interface 注解名 {定义体}

@Override这样,没有成员定义的注解称之为标记注解.

自定义注解后,需要处理注解,注解实现的具体逻辑需要自己来写注解处理器

注解处理器:

注解处理器可以分为运行时注解处理和编译时注解处理器.运行时处理器需要借助反射机制实现,而编译时处理器则需要借助APT来实现.

无论是运行时注解处理器还是编译时注解处理器,主要工作都是读取注解然后处理特定注解

运行时注解处理器

运行时注解属性为@Retention(RetentionPolicy.RUNTIME)的注解.

为了运行时能准确获取到注解的相关信息,Java在java.lang.reflect 反射包下新增了AnnotatedElement接口,所有程序元素的(Class,Method)都实现了该接口,通过该接口提供的方法可以利用反射技术地读取注解的信息

常用AnnotatedElement中的反射方法:

方法 含义
T getAnnotation(Class annotationClass) 返回该元素上存在的制定类型的注解
Annotation[] getAnnotations() 返回该元素上存在的所有注解
default T[] getAnnotationsByType(Class annotationClass) 返回该元素指定类型的注解
default T getDeclaredAnnotation(Class annotationClass) 返回直接存在与该元素上的所有注释
default T[] getDeclaredAnnotationsByType(Class annotationClass) 返回直接存在该元素岸上某类型的注释
Annotation[] getDeclaredAnnotations() 返回直接存在与该元素上的所有注释

通过这些反射方法获取到注解信息,然后对其进行业务操作即可

编译时注解处理器

(Annotation Processor Tool)APT,处理注解来生成代码
获取注解及生成代码都是在代码编译时候完成的,相比反射在运行时处理注解大大提高了程序性能

不同于运行时注解处理器,编写编译时注解处理器分两步:

继承AbstractProcessor,实现自己的注解处理器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class RouteProcessor extends AbstractProcessor {

@Override
public synchronized void init(ProcessingEnvironment env){
super.init(processingEnv);
}

@Override
public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) {
return false;
}

@Override
public Set<String> getSupportedAnnotationTypes() {
return super.getSupportedAnnotationTypes();
}

@Override
public SourceVersion getSupportedSourceVersion() {
return super.getSupportedSourceVersion();
}
}
方法 作用
init(ProcessingEnvironment processingEnv) 该方法有注解处理器自动调用,其中ProcessingEnvironment类提供了很多有用的工具类:Filter,Types,Elements,Messager等
getSupportedAnnotationTypes() 该方法返回字符串的集合表示该处理器用于处理那些注解
getSupportedSourceVersion() 该方法用来指定支持的java版本,一般来说我们都是支持到最新版本,因此直接返回SourceVersion.latestSupported()即可
process(Set annotations, RoundEnvironment roundEnv) 该方法是注解处理器处理注解的主要地方,我们需要在这里写扫描和处理注解的代码,以及最终生成的java文件。其中需要深入的是RoundEnvironment类,该用于查找出程序元素上使用的注解
注册处理器,并打成jar包

注解和注解处理器必须要新建jar包,然后通过jar包引用。

为了让java编译器或能够找到自定义的注解处理器我们需要对其进行注册和打包:自定义的处理器需要被打成一个jar,并且需要在jar包的META-INF/services路径下中创建一个固定的文件javax.annotation.processing.Processor,在javax.annotation.processing.Processor文件中需要填写自定义处理器的完整路径名,有几个处理器就需要填写几个

AbstractProcessor中生成java类,可以使用JavaPoet开源库进行编写,提升效率

常用的Lombok来消除代码冗余,则是通过APT来实现的

分享到: