Annotation 是什么
Annotation (注解) 就是我们平时写代码里经常遇到的 @Override
这种的东西。看到@
字符开头就知道后面跟的是一个注解啦。 在各种开源库里也经常看到注解的身影,为开发提供了大大的便利。
Java Annotation 主要有以下几种用途:
- 为编译器提供信息, 编译器通过注解提供的信息检查代码。
- 编译时或部署时,工具应用可以通过注解信息生成代码或 XML 文件等。
- 运行时注解,在运行时检查注解并进行特殊处理。
注解使用的方式很多,可以放置在类声明,字段,方法或者其他编程元素上。随着 Java 8 的发布,注解还可以用作类型。被称为 type annotation,Learn more 。
1 | // Type Annotation, |
如何创建注解
创建注解和创建一个接口很像,只不过在interface
前多了一个@
。注解使用方式很多,对应的创建方式也各不相同。
最简单的注解
1 | public NanoAnnotation{ |
注解相关的注解
@Retention
:声明被标记的注解如何储存的RetentionPolicy.SOURCE
: 只在源码保留,编译器会忽略RetentionPolicy.CLASS
: 编译器会保留它,JVM 运行时会忽略RetentionPolicy.RUNTIME
: JVM在运行时会使用
@Documented
: 注解默认是不会包含进 Javadoc 的,使用@Documented
可以强制 Javadoc tool 记录被标记的注解。Learn more.@Target
: 限制注解的使用范围ElementType.ANNOTATION_TYPE
: 应用于注解。ElementType.CONSTRUCTOR
: 应用于构造函数。ElementType.FIELD
:应用于字段或属性(包括 Enum 常量)ElementType.LOCAL_VARIABLE
:应用于局部变量。ElementType.METHOD
: 应用于方法级注解。ElementType.PACKAGE
: 应用于 package 。ElementType.PARAMETER
: 应用于方法的参数。ElementType.TYPE
: 应用于 class,interface 或者 enum。ElementType.TYPE_USE
:Use of a type (@since 1.8) 。ElementType.TYPE_PARAMETER
:Type parameter declaration (@since 1.8) 。
@Inherited
: 表示被标记的注解在使用时,其注解的类的子类也继承这个注解。这个特性默认状态是关闭的。@Repeatable
: Java 8 引入,表示被标记的注解在同一位置可以重复使用多次。Learn more.
注解的解析
编译时注解的解析
ButterKnife ButterKnifeProcessor.java
解析编译时注解需要自定义 AbstractProcessor
,其中最主要的就是重写process
方法,在这里可以根据注解生成新的代码。
1 |
|
重写getSupportedAnnotationTypes
可以返回所有支持的注解。1
2
3
4
5
6
7
8
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
types.add(annotation.getCanonicalName());
}
return types;
}
重写getSupportedSourceVersion
可以返回 Processor 支持的版本信息。
1 |
|
注意:想要运行 Processor 需要配合 apt 工具
运行时注解的解析
解析运行时注解需要使用反射。在运行时可以获取到注解相关信息,
Retrofit ServiceMethod.java
下面就是 Retrofit 在解析 @Post
, @Get
这些注解时的代码。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55...
public Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
// 在此获取 Annotation
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
public ServiceMethod build() {
...
for (Annotation annotation : methodAnnotations) {
// 解析所有运行时 Annotation
parseMethodAnnotation(annotation);
}
...
}
...
// 跳转到各个 Annotation 对应的详细解析方法
private void parseMethodAnnotation(Annotation annotation) {
...
else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
}
...
else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
if (headersToParse.length == 0) {
throw methodError("@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError("Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError("Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
...