文章目录
前言
注解是指一些被插入到源文件的标签。Javac对于包含注解或不包含注解的代码会产生相同的字节码,要想使用注解必须提供注解处理工具,这些工具可以在运行时对注解进行处理(反射)、在编译时对注解进行处理(语言模型)或者在字节码层面对注解进行处理(字节码工程)。本文只会描述在编译时对注解进行处理的工具,也就是语言模型和注解处理器,要想使用注解处理器必须了解语言模型API及编译器API。以上本文提及的概念在作者其它文章中都有详细记载,感兴趣可查阅。
注解
注解接口
所有注解都通过注解接口定义,所有注解接口都隐式扩展自java.lang.annotation.Annotation
接口。注解接口不支持继承,并且不支持循环依赖(自身使用自身或者注解A使用了注解B,那么注解B就不能使用注解A)。
modifiers @interface AnnotationName{
type elementName1();
type elementName2() default value;
}
所有注解通过以下形式使用,如果注解元素有默认值,在使用时可以不用为该元素指定值。
@AnnotationName(elementName1=value1,eementName2=value2)
注解元素
所有在注解接口中定义的元素称为注解元素,注解元素必须是以下类型:
- 基本类型
- String
- Class
- enum
- Annotation
- 以上类型组成的数组
默认值
注解元素的值不能为null,可以使用default
为注解元素指定一个默认值:
@interface myAnnotaton{
int intElement() default 1;
String stringElement() default "value";
Class classElement() default String.class;
StandardOpenOption enumElement() default StandardOpenOption.CREATE;
NotNull annotationElement() default @NotNull;
int[] arrayElement()default {};
}
默认值并不和注解存储在一起,而是动态获取的,如果修改注解的默认值,那么已经编译过的类文件中也会使用这个新的默认值。
标记注解
如果一个注解中没有任何元素或者所有元素都有默认值,那么这种注解称为标记注解,在使用时可以不用加括号。
@AnnotationName
单值注解
如果在注解中定义了名为 value
的元素,并且在使用该注解时,value
为唯一一个需要赋值的元素,那么可以直接在括号中给出value
的值。
@AnnotationName(value)
元注解
元注解是用于注解注解的注解。
@Target
@Target
用于限制当前注解可以用在哪些地方,一个没有被@Target
约束的注解可以用在任何地方。
public @interface Target {
ElementType[] value();
}
属性值 | 说明 |
---|---|
ANNOTATION_TYPE | 可以用在注解上 |
CONSTRUCTOR | 可以用在构造器上 |
FIELD | 可以用在字段上 |
LOCAL_VARIABLE | 可以用在局部变量上 |
METHOD | 可以用在方法上 |
PACKAGE | 可以用在包上 |
PARAMETER | 可以用在方法参数上 |
TYPE | 可以用在类和接口上 |
TYPE_PARAMETER | 可以用在泛型参数上 |
TYPE_USE | 类型用法 |
当一个注解被用在局部变量上时只能在运行时处理注解,应为类文件不记录局部变量信息。
@Retention
@Retention
用于指定一条注解应该保留多长时间,默认值是CLASS
。
public @interface Retention {
RetentionPolicy value();
}
属性值 | 说明 |
---|---|
SOURCE | 保留到编译之前 |
CLASS | 保留到运行之前 |
RUNTIME | 保留到运行之后 |
@Documented
@Documented
用于提示归档工具在归档时是否包含此注解。
public @interface Documented {
}
@Inherited
@Inherited
只能应用于被@Target(ElementType.TYPE)
注解的注解,该注解的作用是允许子类继承被该注解注解的注解注解的父类时继承被该注解注解的注解。
public @interface Inherited {
}
@Repeatable
@Repeatable
用于指定当前注解可以在同一地方使用多次。
public @interface Repeatable {
Class<? extends Annotation> value();
}
它的value
元素指定当前注解的一个容器注解,并且此容器注解的保存时间必须大于等于子注解:
@Repeatable(MyAnnotations.class)
@interface MyAnnotation{
}
@interface MyAnnotations{
MyAnnotation[] value();
}
注解处理器
Processor代表注解处理器的接口,注解处理器可以在编译时处理注解,因为注解处理功能被集成到了Javac中,通过以下命令就可以在编译时调用注解处理器处理注解:
javac -processor <processorClassNames> <src>
编译器会定位源文件中的注解,每个注解处理器都会依次执行,并得到它们感兴趣的注解。注解处理器只能创建源文件,不能在已有源文件上进行修改。如果一个注解处理器创建了一个新的源文件,那么上述过程就会重复执行,如果某次循环没有再产生任何新的源文件,那么就编译所有源文件。
void init(ProcessingEnvironment processingEnv)//该方法会被注解处理工具调用,并输入ProcessingEnviroment参数。
boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)//真正处理注解的方法,全部处理完成返回true
Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText)
Set<String> getSupportedAnnotationTypes()//指定注解处理器可以处理的注解
Set<String> getSupportedOptions()//指定注解处理器支持的选项,集合中返回的每个字符串必须是一个以句号分隔的标识符序列。
SourceVersion getSupportedSourceVersion()//指定源代码的Java版本
上文最后三个方法可以使用以下三个注解代替:
注解 |
---|
@SupportedAnnotationTypes |
@SupportedOptions |
@SupportedSourceVersion |
在床架注解处理器时,通常继承AbstractProcessor
类。
RoundEnvironment
注解处理框架为注释处理程序提供一个实现该接口的对象,以便处理程序可以查询关于一轮注解处理的信息。
boolean errorRaised()
Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a)
Set<? extends Element> getElementsAnnotatedWith(TypeElement a)
Returns the elements annotated with the given annotation type.
Set<? extends Element> getRootElements()
boolean processingOver()
ProcessingEnvironment
ProcessingEnviroment提供很多有用的工具类Elements, Types和Filer。以便处理程序可以使用工具来编写新文件、报告错误消息和查找其它实用程序。
Elements getElementUtils()
Filer getFiler()
Types getTypeUtils()
Locale getLocale()
Messager getMessager()
Map<String,String> getOptions()
SourceVersion getSourceVersion()
Filer
Filer支持注释处理器创建新文件。可以区分三种文件:源文件、类文件和辅助资源文件。
JavaFileObject createClassFile(CharSequence name, Element... originatingElements)
FileObject createResource(JavaFileManager.Location location, CharSequence pkg, CharSequence relativeName, Element... originatingElements)
JavaFileObject createSourceFile(CharSequence name, Element... originatingElements)
FileObject getResource(JavaFileManager.Location location, CharSequence pkg, CharSequence relativeName)
Messager
Messager提供了注解处理器报告错误消息、警告和其他通知的方式。
void printMessage(Diagnostic.Kind kind, CharSequence msg)
void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e)
void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e, AnnotationMirror a)
void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e, AnnotationMirror a, AnnotationValue v)
Completion
Completion表示注解的注释。
String getMessage()
String getValue()
Completions
用于封装Completion对象的工具类。
static Completion of(String value)
static Completion of(String value, String message)
demo
代码地址如下:
https://github.com/chinesecooly/annotation-processor.git