Java
注解的解析有两种方式:反射和注解处理器(APT/JSR 269
),本文主要介绍注解处理器解析方式。
解析方式 注解处理器解析注解的方式,是在编译期解析的,所以对注解的 Rentention
没有要求(即使 RetentionPolicy.SOURCE
也可以),可以根据编码需求自行设定。
工具属性 注解处理器是 javac
的一个工具,它用来在编译时扫描和处理对应注解。一个注解的注解处理器,通常以 Java
代码(或者编译过的字节码)作为输入,生成文件(通常是 .java
文件)作为输出。这些生成的 Java
代码是在新生成的 .java
文件中,所以不能修改已经存在的 Java
类,例如向已有的类中添加方法。这些生成的 .java
文件,会同其他普通的手动编写的 Java
源代码一样被 javac
编译。
独立进程 注解处理器是运行它自己的虚拟机 JVM
中的,javac
启动一个完整 Java
虚拟机来运行注解处理器,所以可以把注解处理器库看做一个独立的 Java
项目。
自定义注解处理器流程 自定义注解和自定义注解处理器建议分成两个 jar
包,自定义注解处理器仅仅在编译期需要用到,所以工程中不需要将处理器打包到目标代码中。
自定义注解库 Custom Annotation
在 AS
中新建 Java
库:File --> New --> New Module --> Java Libary
,新建 myanno
注解库。
Gradle
文件:
1 2 3 4 5 6 7 8 9 10 apply plugin: 'java-library' dependencies { implementation fileTree (dir: 'libs' , include : ['*.jar' ]) } sourceCompatibility = "1.8" targetCompatibility = "1.8"
注解源文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Retention(RetentionPolicy.SOURCE) @Target(ElementType.FIELD) public @interface Field { String value () ; } @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface Info { String author () ; String date () ; int version () default 1 ; String[] mails(); } @Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.RUNTIME) public @interface AnyAnnotation { String value () ; }
Gradle
中选择该库,编译生成对应的 jar: myanno
。
自定义注解处理器 Custom Processor
同样,需要新建一个自定义处理器 Java
库:processors
。自定义注解处理器有两个步骤:
自定义注解处理器必须继承 AbstractProcessor
库的资源文件夹中添加 javax.annotation.processing.Processor
文件
继承 AbstractProcessor
自定义注解处理器必须继承 AbstractProcessor
,使用 @SupportedAnnotationTypes
定义支持解析的注解,并重写 process
处理对应注解。
1 2 3 4 5 6 7 8 9 10 @SupportedAnnotationTypes({"com.ymzs.annotation.Field"}) public class AnnotationParsingProcessor extends AbstractProcessor { @Override public boolean process (Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { System.out.println("AnnotationParsingProcessor::process" ); return false ; } }
添加 javax.annotation.processing.Processor
文件 为了将自定义注解处理器注册到 javac
时,必须打包一个特殊的文件 javax.annotation.processing.Processor
到 META-INF/services
目录下。原因见 Java 技术手册 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 // 源码目录结构 main -java -com -ymzs -parsing -AnnotationParsingProcessor.java -resources -META-INF -services -javax.annotation.processing.Processor // jar 包目录结构 processors.jar -com -ymzs -parsing -AnnotationParsingProcessor.class -META-INF -services -javax.annotation.processing.Processor -MANIFEST.MF
javax.annotation.processing.Processor
文件的内容是一个列表,每一行对应一个注解处理器的全称。例如:
1 2 com.ymzs.parsing.AnnotationParsingProcessor com.ymzs.parsing.OtherProcessor
Gradle
中选择该库,编译生成对应的 jar: processors
。
注解处理器插件 在 AS
中使用自定义注解处理器需要添加对应的 gradle
插件:
1 2 3 4 5 6 7 8 9 10 11 12 13 plugins{ id 'java-library' id "net.ltgt.apt" version "0.15" } dependencies { ... implementation project (":myanno" ) annotationProcessor project (":processors" ) }
Android
环境Google
在发布 AS
时,开发了对应的 android-gradle-plugin
,这是一系列插件包,并包含注解处理器插件:Android 注解处理器插件使用方法 。 使用该插件时,被注解代码工程的 gradle
文件中需要包含如下信息,关键词 annotationProcessor
:
1 2 3 4 5 6 7 8 9 10 11 apply plugin: 'com.android.application' dependencies { ... implementation project (":myanno" ) annotationProcessor project (":processors" ) }
结果验证 在 Gradle Projects
窗口中,选择需要被注解代码工程,选择 Tasks --> build --> assemble
编译工程,能正确的打印自定义注解处理器中的 Log
:
1 2 3 4 // 代码工程编译期,解析注解 :javabase:compileJava // 自定义注解处理器解析注解 AnnotationParsingProcessor::process
APT
和 JSR269
APT
APT: Annotation-Processing Tool
,相关 API
都在 com.sun.mirror
包下。从 Java 7
开始就被降级了,在 Java 8
被彻底移除,APT 移除原因 。
JSR269
JSR 269 API: Pluggable Annotation Processing API
,相关 API
都在 javax.annotation.processing, javax.lang.model
包下。从 Java 6
开始引入,后续版本逐步代替了 APT
。JSR 269
不管是那种 API
,它们都是处理注解的工具,对源代码文件进行检测,并找出对应注解后解析(或生成对应文件)。通常用来简化开发者的工作量,或者生成附属文件。虽然 APT
被移除了,但是因为历史原因,注解处理器还是常用 APT
来称呼。本文都是基于 JSR 269 API
实现的自定义注解处理器。
AnnotatedConstruct
体系类图结构
AnnotatedConstruct
基本类AnnotatedConstruct JavaDoc
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 public interface AnnotatedConstruct { List<? extends AnnotationMirror> getAnnotationMirrors(); <A extends Annotation> A getAnnotation (Class<A> var1) ; <A extends Annotation> A[] getAnnotationsByType(Class<A> var1); } public interface AnnotationMirror { DeclaredType getAnnotationType () ; Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValues(); } public interface AnnotationValue { Object getValue () ; String toString () ; <R, P> R accept (AnnotationValueVisitor<R, P> var1, P var2) ; }
TypeMirror
体系TypeMirror JavaDoc
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 public enum TypeKind { BOOLEAN, BYTE, SHORT, INT, LONG, CHAR, FLOAT, DOUBLE, VOID, NONE, NULL, ARRAY, DECLARED, ERROR, TYPEVAR, WILDCARD, PACKAGE, EXECUTABLE, OTHER, UNION, INTERSECTION; private TypeKind () { } public boolean isPrimitive () { switch (this ) { case BOOLEAN: case BYTE: case SHORT: case INT: case LONG: case CHAR: case FLOAT: case DOUBLE: return true ; default : return false ; } } } public interface TypeMirror extends AnnotatedConstruct { TypeKind getKind () ; boolean equals (Object var1) ; int hashCode () ; String toString () ; <R, P> R accept (TypeVisitor<R, P> var1, P var2) ; } public interface IntersectionType extends TypeMirror { List<? extends TypeMirror> getBounds(); } public interface UnionType extends TypeMirror { List<? extends TypeMirror> getAlternatives(); } public interface ExecutableType extends TypeMirror { List<? extends TypeVariable> getTypeVariables(); TypeMirror getReturnType () ; List<? extends TypeMirror> getParameterTypes(); TypeMirror getReceiverType () ; List<? extends TypeMirror> getThrownTypes(); } public interface NoType extends TypeMirror {}public interface PrimitiveType extends TypeMirror {}public interface WildcardType extends TypeMirror { TypeMirror getExtendsBound () ; TypeMirror getSuperBound () ; } public interface ReferenceType extends TypeMirror {}public interface ArrayType extends ReferenceType { TypeMirror getComponentType () ; } public interface NullType extends ReferenceType {}public interface TypeVariable extends ReferenceType { Element asElement () ; TypeMirror getUpperBound () ; TypeMirror getLowerBound () ; } public interface DeclaredType extends ReferenceType { Element asElement () ; TypeMirror getEnclosingType () ; List<? extends TypeMirror> getTypeArguments(); } public interface ErrorType extends DeclaredType {}
Element
体系Element JavaDoc
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 public enum ElementKind { PACKAGE, ENUM, CLASS, ANNOTATION_TYPE, INTERFACE, ENUM_CONSTANT, FIELD, PARAMETER, LOCAL_VARIABLE, EXCEPTION_PARAMETER, METHOD, CONSTRUCTOR, STATIC_INIT, INSTANCE_INIT, TYPE_PARAMETER, OTHER, RESOURCE_VARIABLE; private ElementKind () { } public boolean isClass () { return this == CLASS || this == ENUM; } public boolean isInterface () { return this == INTERFACE || this == ANNOTATION_TYPE; } public boolean isField () { return this == FIELD || this == ENUM_CONSTANT; } } public enum Modifier { PUBLIC, PROTECTED, PRIVATE, ABSTRACT, DEFAULT, STATIC, FINAL, TRANSIENT, VOLATILE, SYNCHRONIZED, NATIVE, STRICTFP; private Modifier () { } public String toString () { return this .name().toLowerCase(Locale.US); } } public enum NestingKind { TOP_LEVEL, MEMBER, LOCAL, ANONYMOUS; private NestingKind () { } public boolean isNested () { return this != TOP_LEVEL; } } public interface Element extends AnnotatedConstruct { TypeMirror asType () ; ElementKind getKind () ; Set<Modifier> getModifiers () ; Name getSimpleName () ; Element getEnclosingElement () ; List<? extends Element> getEnclosedElements(); boolean equals (Object var1) ; int hashCode () ; List<? extends AnnotationMirror> getAnnotationMirrors(); <A extends Annotation> A getAnnotation (Class<A> var1) ; <R, P> R accept (ElementVisitor<R, P> var1, P var2) ; } public interface QualifiedNameable extends Element { Name getQualifiedName () ; } public interface PackageElement extends Element , QualifiedNameable { Name getQualifiedName () ; Name getSimpleName () ; List<? extends Element> getEnclosedElements(); boolean isUnnamed () ; Element getEnclosingElement () ; } public interface VariableElement extends Element { Object getConstantValue () ; Name getSimpleName () ; Element getEnclosingElement () ; } public interface TypeParameterElement extends Element { Element getGenericElement () ; List<? extends TypeMirror> getBounds(); Element getEnclosingElement () ; } public interface Parameterizable extends Element { List<? extends TypeParameterElement> getTypeParameters(); } public interface ExecutableElement extends Element , Parameterizable { List<? extends TypeParameterElement> getTypeParameters(); TypeMirror getReturnType () ; List<? extends VariableElement> getParameters(); TypeMirror getReceiverType () ; boolean isVarArgs () ; boolean isDefault () ; List<? extends TypeMirror> getThrownTypes(); AnnotationValue getDefaultValue () ; Name getSimpleName () ; } public interface TypeElement extends Element , Parameterizable , QualifiedNameable { List<? extends Element> getEnclosedElements(); NestingKind getNestingKind () ; Name getQualifiedName () ; Name getSimpleName () ; TypeMirror getSuperclass () ; List<? extends TypeMirror> getInterfaces(); List<? extends TypeParameterElement> getTypeParameters(); Element getEnclosingElement () ; }
源代码的每一个部分都是一个特定类型的 Element
,也就是说 Element
代表程序的任意元素,例如包、类、方法等。每个 Element
代表一个静态的、语言级别的构件。
1 2 3 4 5 6 7 8 9 10 package com.example; public class Foo { private int a; private Foo other; public Foo () {} public void setA ( // ExecuteableElement int newA // TypeElement ) {}}
换个角度来看源代码,它只是结构化的文本,不是可运行的。可以想象它就像要被解析的 XML
文件一样(或者是编译器中抽象的语法树)。就像 XML
解释器一样,有一些类似 DOM
的元素,可以从一个元素导航到它的父或者子元素上。 举例来说,假如有一个代表 public class Foo
类的 TypeElement
元素,可以遍历它的所有元素:
1 2 3 4 TypeElement fooClass = ... ; for (Element e : fooClass.getEnclosedElements()){ Element parent = e.getEnclosingElement(); }
Elements
接口用来处理 Element
的工具类,Elements JavaDoc 。
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 public interface Elements { PackageElement getPackageElement (CharSequence name) ; TypeElement getTypeElement (CharSequence name) ; Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValuesWithDefaults(AnnotationMirror var1); String getDocComment (Element var1) ; boolean isDeprecated (Element var1) ; Name getBinaryName (TypeElement var1) ; PackageElement getPackageOf (Element var1) ; List<? extends Element> getAllMembers(TypeElement var1); List<? extends AnnotationMirror> getAllAnnotationMirrors(Element var1); boolean hides (Element hider, Element hidden) ; boolean overrides (ExecutableElement overrider, ExecutableElement overridden, TypeElement type) ; String getConstantExpression (Object var1) ; void printElements (Writer var1, Element... var2) ; Name getName (CharSequence var1) ; boolean isFunctionalInterface (TypeElement var1) ; }
Types
接口用来处理 TypeMirror
的工具类,Types JavaDoc 。
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 public interface Types { Element asElement (TypeMirror var1) ; boolean isSameType (TypeMirror var1, TypeMirror var2) ; boolean isSubtype (TypeMirror var1, TypeMirror var2) ; boolean isAssignable (TypeMirror var1, TypeMirror var2) ; boolean contains (TypeMirror var1, TypeMirror var2) ; boolean isSubsignature (ExecutableType var1, ExecutableType var2) ; List<? extends TypeMirror> directSupertypes(TypeMirror var1); TypeMirror erasure (TypeMirror var1) ; TypeElement boxedClass (PrimitiveType var1) ; PrimitiveType unboxedType (TypeMirror var1) ; TypeMirror capture (TypeMirror var1) ; PrimitiveType getPrimitiveType (TypeKind var1) ; NullType getNullType () ; NoType getNoType (TypeKind var1) ; ArrayType getArrayType (TypeMirror var1) ; WildcardType getWildcardType (TypeMirror extendsBound, TypeMirror superBound) ; DeclaredType getDeclaredType (TypeElement typeElem, TypeMirror... typeArgs) ; DeclaredType getDeclaredType (DeclaredType containing, TypeElement typeElem, TypeMirror... typeArgs) ; TypeMirror asMemberOf (DeclaredType containing, Element element) ; }
Filer
接口用来创建文件,Filer JavaDoc 。
1 2 3 4 5 6 7 8 9 10 public interface Filer { JavaFileObject createSourceFile (CharSequence var1, Element... var2) throws IOException ; JavaFileObject createClassFile (CharSequence var1, Element... var2) throws IOException ; FileObject createResource (Location var1, CharSequence var2, CharSequence var3, Element... var4) throws IOException ; FileObject getResource (Location var1, CharSequence var2, CharSequence var3) throws IOException ;}
Messager
接口提供给注解处理器一个报告错误、警告以及提示信息的途径。Messager JavaDoc , 消息级别 Diagnostic.Kind 。 因为注解处理器是在独立的 java
虚拟机运行,所以注解处理器中不能直接进行异常抛出,否则进程异常崩溃时,会弹出一大堆让人捉摸不清的堆栈调用日志显示,也就是目标工程报的错误来自于另外一个虚拟机的堆栈。通常使用 Messager
来写一些信息给使用此注解器的第三方开发者的。在官方文档中描述了消息的不同级别,非常重要的是 Kind.ERROR
,因为这种类型的信息用来表示我们的注解处理器处理失败了,很有可能是第三方开发者错误的使用了注解。
1 2 3 4 5 6 7 8 9 public interface Messager { void printMessage (Kind var1, CharSequence var2) ; void printMessage (Kind var1, CharSequence var2, Element var3) ; void printMessage (Kind var1, CharSequence var2, Element var3, AnnotationMirror var4) ; void printMessage (Kind var1, CharSequence var2, Element var3, AnnotationMirror var4, AnnotationValue var5) ;}
AbstractProcessor
详细分析自定义处理器通常会继承 AbstractProcessor
,并重写对应方法,来实现自定义注解的解析。所有的注解处理器类都必须有一个无参构造函数 ,否则执行时会报错。 对应源码文件目录:src\javax\annotation\processing
类图结构
常用 API
介绍 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 public interface Processor { Set<String> getSupportedOptions () ; Set<String> getSupportedAnnotationTypes () ; SourceVersion getSupportedSourceVersion () ; void init (ProcessingEnvironment processingEnv) ; boolean process (Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) ; Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText); } public abstract class AbstractProcessor implements Processor { ... public abstract boolean process (Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) ; ... }
两个有用的注解
循环调用 process
process
通常会被执行两次:根据 Log
可以发现,process
的返回值不管是 true/false
都会被执行两次?但是解析注解时,只有第一次执行 process
时,roundEnvironment.getElementsAnnotatedWith
才能获取到注解。 具体原因参考 Processor JavaDoc 。官方文档中第一段就介绍了,注解处理器是个循环处理的过程,每次循环都会解析上次处理器产生的源文件。也就是说第一次 process
处理了编写的源文件,第二次处理了注解处理器生成的源文件(此时已经不包含被处理的注解了),直到所有支持的注解都被解析完毕。
存在的问题 不管是使用 @SupportedAnnotationTypes
还是重写 getSupportedAnnotationTypes()
,只要支持注解包其中一个注解,注解处理器就能解析该包中的所有其他注解。
1 2 3 4 // Java library: myanno @Info @Field @AnyAnnotation
注解包 myanno
包含三个注解,但是在自定义注解器中,代码中规定只支持 Field
注解,自定义注解器仍然能解析 Info, AnyAnnotation
?
注解处理器插件
gradle-apt-plugin Java
环境下注解插件,使用方法: 1 2 3 4 5 6 7 8 9 plugins{ id "net.ltgt.apt" version "0.15" } dependencies { annotationProcessor project (":processors" ) annotationProcessor "com.custom:CustomProcessors:version1.2.3" }
android-apt Android
注解器框架,现在已经不再维护。
annotationProcessor
Android Plugin for Gradle
,google
为 gradle
开发的注解处理器框架,用于替换 android-apt
。使用方法: 1 2 3 4 annotationProcessor ":customProcessors" annotationProcessor "com.custom:CustomProcessors:version1.2.3"
常用开源库
AutoService
官网 自动生成 javax.annotation.processing.Processor
文件,并将自定义注解处理器按照规范加入该文件。
JavaPoet
官网 生成 .java
源文件的 API
接口,非常强大实用。解析自定义注解时,可以使用 JavaPoet
来生成对应文件。
AutoService
用法
build.gradle
文件中添加依赖implementation 'com.google.auto.service:auto-service:1.0-rc4'
具体版本号,到 github
官网上查看。
自定义注解处理器类上添加 AutoService
注解 在自定义处理器类上,添加 @AutoService(Processor.class)
注解。该注解会自动生成对应的 META-INF/services/javax.annotation.processing.Processor
文件,并将自定义注解处理器按照规范加入该文件。
JavaPoet
用法
其他 调试注解处理器
常用场景 大量开源库都使用了注解处理器简化代码:
参考文档