是什么
编译时注解是一种只在编译期间生效的注解。比如常见的@Override
。
@Override的定义如下:1
2
3
4@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
- @Target(ElementType.METHOD) 表示此注解只能用在方法上。
- @Retention(RetentionPolicy.SOURCE) 表示此注解只存在于源码中。
@Retention(RetentionPolicy.RUNTIME) 表示这是一个运行时注解
为什么
我们为什么要使用编译时注解呢?在什么情况下使用呢?
使用案例
- 【打印日志】可以用于打印方法的出参、入参和耗时。
- 【管理缓存】可以缓存方法的返回值,也可以删除方法的缓存。
- 【方法限流】可以限制方法的访问量和访问频率。
- 【监控预警】可以用于监控接口的调用。
- 【防重复提交】可以用于防重复提交。
- …
优点
- 【比运行时注解快】因为运行时注解需要结合反射,而编译时注解只是消耗编译时性能。
- 【运用范围广】因为她不依赖任何环境,比如spring环境。她能在main方法中使用。
怎么用
本例以maven项目演示。
pom.xml
1 | <build> |
<compilerArgument>-proc:none</compilerArgument>
表示不使用默认的处理器(因为我们要使用自定义的处理器)。
注册注解处理器
在/src/main/resources/META-INF/services/
目录下创建文件javax.annotation.processing.Processor
, 内容为:1
com.github.ofofs.jca.processor.JcaProcessor
JcaProcessor是我们自定义的注解处理器,请注意修改为自己的包名。
注解处理器
顾名思义,它就是用来处理注解的,就像我们在使用运行时注解的时候,也会在切面方法中去处理注解一样。注解定义好了,当然要去处理,不然要她做甚。
JcaProcessor.java
: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
28package com.github.ofofs.jca.processor;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import java.util.Set;
/**
* JCA编译时注解处理器
*
* @author kangyonggan
* @since 6/22/18
*/
@SupportedAnnotationTypes("com.github.ofofs.jca.annotation.Serial")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class JcaProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment env) {
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
// 可以在这里处理自定义编译时注解@Serial
return true;
}
}
SupportedAnnotationTypes
, 它的value是一个数组,表明我们可以在一个注解处理器中同时处理多个注解。- 要注意init方法中的env和process方法中的env不是同一种。
实例
我想定义一个@Serial的编译时注解,只作用在类上,它的功能是给这个类实现序列化接口(implements Serializable), 并且生成唯一的serialVersionUID。即:
源码:
1 | package com.github.ofofs.jca.serial; |
编译后:
1 | // |
感觉是不是很炫酷?很神奇?很强大?其实编译时注解的强大远远超乎你的想象。本篇文章不会贴出此注解的实现代码。下篇文章我将会一一介绍处理编译时注解的各个技巧。