注解是指一些被插入到源文件的标签。Javac对于包含注解或不包含注解的代码会产生相同的字节码,要想使用注解必须提供注解处理工具,这些工具可以在运行时对注解进行处理(反射)、在编译时对注解进行处理(注解处理器)或者在字节码层面对注解进行处理(字节码工程)。本文将详细描述注解处理器的使用方法。
所有注解都通过注解接口定义,所有注解接口都隐式扩展自java.lang.annotation.Annotation
。注解接口不支持继承,并且不支持循环依赖(循环依赖是指自身使用自身或者注解A使用了注解B,那么注解B就不能使用注解A)。
modifiers @interface AnnotationName{
type elementName1();
type elementName2() default value;
}
所有注解通过以下形式使用,如果注解元素有默认值,在使用时可以不用为该元素指定值。
@AnnotationName(elementName1=value1,eementName2=value2)
所有在注解接口中定义的元素称为注解元素,注解元素必须是以下类型:
注解元素的值不能为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
约束的注解可以用在任何地方。
public @interface Target {
ElementType[] value();
}
属性值 | 说明 |
---|---|
ANNOTATION_TYPE | 可以用在注解上 |
CONSTRUCTOR | 可以用在构造器上 |
FIELD | 可以用在字段上 |
LOCAL_VARIABLE | 可以用在局部变量上 |
METHOD | 可以用在方法上 |
PACKAGE | 可以用在包上 |
PARAMETER | 可以用在方法参数上 |
TYPE | 可以用在类和接口上 |
TYPE_PARAMETER | 可以用在泛型参数上 |
TYPE_USE | 类型用法 |
当一个注解被用在局部变量上时只能在运行时处理注解,应为类文件不记录局部变量信息。
@Retention
用于指定一条注解应该保留多长时间,默认值是CLASS
。
public @interface Retention {
RetentionPolicy value();
}
属性值 | 说明 |
---|---|
SOURCE | 保留到编译之前 |
CLASS | 保留到运行之前 |
RUNTIME | 保留到运行之后 |
@Documented
用于提示归档工具在归档时是否包含此注解。
public @interface Documented {
}
@Inherited
只能应用于被@Target(ElementType.TYPE)
注解的注解,该注解的作用是允许子类继承被该注解注解的注解注解的父类时继承被该注解注解的注解。
public @interface Inherited {
}
@Repeatable
用于指定当前注解可以在同一地方使用多次。
public @interface Repeatable {
Class<? extends Annotation> value();
}
它的value
元素指定当前注解的一个容器注解,并且此容器注解的保存时间必须大于等于子注解:
@Repeatable(MyAnnotations.class)
@interface MyAnnotation{
}
@interface MyAnnotations{
MyAnnotation[] value();
}
Tool是所有可以从程序中调用的工具的公共接口。该接口的run
方法使用给定的I/O通道和参数运行该工具。返回0表示成功,返回非0表示错误。生成的任何诊断将以某种未指定的格式写入out或err。
/**
*in:如果为空则使用标准输入
*out:如果为空则使用标准输出
*err:如果为空则使用标准错误
*arguments:该工具运行时需要的参数
*/
int run(InputStream in, OutputStream out, OutputStream err, String... arguments)
JavaCompiler是从程序中调用javac编译器的接口。
/**
*diagnosticListener:用于非致命诊断的诊断侦听器,如果为空,则使用编译器的默认方法来报告诊断
*locale:格式化诊断时要应用的语言环境;Null表示默认区域设置。
*charset:用于解码字节的字符集,如果为空,则使用平台默认值。
*/
StandardJavaFileManager getStandardFileManager(DiagnosticListener<? super JavaFileObject> diagnosticListener, Locale locale, Charset charset)
/**
*out:一个Writer用于编译器的额外输出,如果为零则默认为标准错误流
*fileManager:文件管理器,如果为空则默认使用编译器的标准管理器
*diagnosticListener:诊断侦听器;如果为空,则使用编译器的默认方法来报告诊断
*options:编译器选项
*classes:注释处理要处理的类名,空表示没有类名
*compilationUnits:要编译的编译单元,null表示没有编译单元
*/
JavaCompiler.CompilationTask getTask(Writer out, JavaFileManager fileManager, DiagnosticListener<? super JavaFileObject> diagnosticListener, Iterable<String> options, Iterable<String> classes, Iterable<? extends JavaFileObject> compilationUnits)
该接口的getTask
方法可以建立一个编译任务,该任务接口继承了Callabel接口,并提供了以下方法设置一个注解处理器。
void setProcessors(Iterable<? extends Processor> processors)
从程序调用javaDoc文档的接口。
void setLocale(Locale locale)
工具的文件抽象。
boolean delete()//删除文件对象
CharSequence getCharContent(boolean ignoreEncodingErrors)//获取此文件对象的字符内容
long getLastModified()//获取最后修改时间
String getName()//获取吗名称
InputStream openInputStream()
OutputStream openOutputStream()
Reader openReader(boolean ignoreEncodingErrors)
Writer openWriter()
URI toUri()
用于在Java源文件和字节码文件上操作的文件抽象。
Modifier getAccessLevel()//提供有关此文件对象所表示的类的访问级别的提示。
JavaFileObject.Kind getKind()//获取此文件对象的类型。
NestingKind getNestingKind()//提供有关此文件对象所表示的类的嵌套级别的提示。
boolean isNameCompatible(String simpleName, JavaFileObject.Kind kind)//检查该文件对象是否与指定的简单名称和类型兼容。
SimpleJavaFileObject
为JavaFileObject中的大多数方法提供简单的实现。
将调用转发给给定的文件对象。该类的子类可能会覆盖其中一些方法,还可能提供额外的字段和方法。
ForwardingJavaFileObject
将调用转发给给定的文件对象。该类的子类可能会覆盖其中一些方法,还可能提供额外的字段和方法。
用于在Java源文件和字节码文件上操作的工具的文件管理器。
void close()//直接或间接释放此文件管理器打开的任何资源。
void flush()/直接或间接刷新此文件管理器打开用于输出的任何资源。
ClassLoader getClassLoader(JavaFileManager.Location location)//获取用于从给定位置装入插件的类装入器。
FileObject getFileForInput(JavaFileManager.Location location, String packageName, String relativeName)//获取一个文件对象,用于表示给定位置中指定包中的指定相对名称的输入。
FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relativeName, FileObject sibling)//获取用于输出的文件对象,该文件对象表示给定位置中指定包中的指定相对名称。
JavaFileObject getJavaFileForInput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind)//获取用于输入的文件对象,该文件对象表示给定位置中指定类型的指定类。
JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling)//获取用于输出的文件对象,该文件对象表示给定位置中指定类型的指定类。
boolean handleOption(String current, Iterator<String> remaining)
boolean hasLocation(JavaFileManager.Location location)//确定此文件管理器是否知道某个位置。
String inferBinaryName(JavaFileManager.Location location, JavaFileObject file)//根据位置推断文件对象的二进制名称。
boolean isSameFile(FileObject a, FileObject b)//比较两个文件对象,如果它们代表相同的底层对象则返回true。
Iterable<JavaFileObject> list(JavaFileManager.Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse)/列出在给定位置中匹配给定条件的所有文件对象。
基于java.io.File的文件管理器。
Iterable<? extends JavaFileObject> getJavaFileObjects(File... files)//获取表示给定文件的文件对象。
Iterable<? extends JavaFileObject> getJavaFileObjects(String... names)//获取表示给定文件名的文件对象。
Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(Iterable<? extends File> files)
Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names)
Iterable<? extends File> getLocation(JavaFileManager.Location location)//获取与给定位置关联的路径。
void setLocation(JavaFileManager.Location location, Iterable<? extends File> path)//将给定路径与给定位置关联起来。
将调用转发给给定的文件管理器。该类的子类可能会覆盖其中一些方法,还可能提供额外的字段和方法。
从工具诊断的接口。诊断通常报告源文件中特定位置的问题。然而,并不是所有的诊断都与位置或文件相关联。
String getCode()//获取诊断码
long getColumnNumber()
long getEndPosition()
Diagnostic.Kind getKind()
long getLineNumber()
String getMessage(Locale locale)
long getPosition()
S getSource()
long getStartPosition()
从工具接收诊断的接口。
void report(Diagnostic<? extends S> diagnostic)
提供在列表中收集诊断信息的简单方法。
List<Diagnostic<? extends S>> getDiagnostics()
工具提供类。
static DocumentationTool getSystemDocumentationTool()
static JavaCompiler getSystemJavaCompiler()
static ClassLoader getSystemToolClassLoader()
AnnotatedConstruct表示可以被注解修饰的构造,构造可以是元素或类型。该接口将注解元素分为不同的类型,如果有注解元素A,那么它的类型为AT,如果AT是一个可重复的注解,那么它包含的注解的类型为ATC,则A的类型为:
<A extends Annotation> A getAnnotation(Class<A> annotationType)
List<? extends AnnotationMirror> getAnnotationMirrors()
<A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType)
AnnotationMirror表示一个注解元素,AnnotationMirror将注解元素的元素和注解的类型联系起来。
DeclaredType getAnnotationType()//获取注解的类型
Map<? extends ExecutableElement,? extends AnnotationValue> getElementValues()//获取注解元素的元素与其对应的值,只包括那些在注解中显式设置值的元素,而不包括那些隐式的默认值的元素,如果想要填充默认值的元素,使用Elements.getElementValuesWithDefaults()方法.
AnnotationValue表示注解元素的元素。
<R,P> R accept(AnnotationValueVisitor<R,P> v, P p)
Object getValue()//获取元素的值
注解类型元素的元素的值的访问器,用于在编译时对未知类型的值进行操作。
R visit(AnnotationValue av)
R visit(AnnotationValue av, P p)
R visitAnnotation(AnnotationMirror a, P p)
R visitArray(List<? extends AnnotationValue> vals, P p)
R visitBoolean(boolean b, P p)
R visitByte(byte b, P p)
R visitChar(char c, P p)
R visitDouble(double d, P p)
R visitEnumConstant(VariableElement c, P p)
R visitFloat(float f, P p)
R visitInt(int i, P p)
R visitLong(long i, P p)
R visitShort(short s, P p)
R visitString(String s, P p)
R visitType(TypeMirror t, P p)
R visitUnknown(AnnotationValue av, P p)
Element接口表示一个程序元素,要实现基于Element对象类的操作,要么使用访问器,要么使用getKind()方法的结果。
<R,P> R accept(ElementVisitor<R,P> v, P p)//将访问器应用于此元素
TypeMirror asType()//返回此元素定义的类型
List<? extends Element> getEnclosedElements()//返回包含在该元素内的其它元素
Element getEnclosingElement()//返回包含该元素的最内层元素
ElementKind getKind()//返回该元素的类型
Set<Modifier> getModifiers()//返回该元素的修饰符(不包括注解)
Name getSimpleName()//返回该元素的简单名称
List<? extends TypeParameterElement> getTypeParameters()//返回该元素的类型变量
QualifiedNameable代表具有限定名称的元素。
Name getQualifiedName()//返回该元素的全限定名
PackageElement表示程序包元素。
boolean isUnnamed()//是否命名
TypeElement表示类或接口程序元素,枚举类型看作类,注解类型看作接口。
List<? extends TypeMirror> getInterfaces()//返回该元素的直接实现或继承的接口类型
NestingKind getNestingKind()//返回该元素的嵌套类型
TypeMirror getSuperclass()//返回该类型的超类
ExecutableElement表示类或接口的构造函数、方法和初始化程序。
AnnotationValue getDefaultValue()//如果该元素是注解元素则直接返回默认值
List<? extends VariableElement> getParameters()//返回该元素的形式参数
TypeMirror getReceiverType()//返回该元素的接收类型
TypeMirror getReturnType()//返回该元素的返回类型,
List<? extends TypeMirror> getThrownTypes()//返回该元素声明的异常
boolean isDefault()//是否是默认方法
boolean isVarArgs()//是否接收可变参数
VariableElement表示表示字段、枚举常量、方法或构造函数参数、局部变量或异常参数元素。
Object getConstantValue()//如果该元素是编译期常量,则返回该元素的值
TypeParameterElement表示泛型类、方法、构造函数声明的类型变量元素。
List<? extends TypeMirror> getBounds()//返回类型变量的边界
Element getGenericElement()//返回由该类型变量泛化的类型元素
用于在编译时未知元素类型时对元素进行操作。
R visit(Element e)
R visit(Element e, P p)
R visitExecutable(ExecutableElement e, P p)
R visitPackage(PackageElement e, P p)
R visitType(TypeElement e, P p)
R visitTypeParameter(TypeParameterElement e, P p)
R visitUnknown(Element e, P p)
R visitVariable(VariableElement e, P p)
用于操作程序元素的实用程序方法。
List<? extends AnnotationMirror> getAllAnnotationMirrors(Element e)
List<? extends Element> getAllMembers(TypeElement type)
Name getBinaryName(TypeElement type)
String getConstantExpression(Object value)
String getDocComment(Element e)
Map<? extends ExecutableElement,? extends AnnotationValue> getElementValuesWithDefaults(AnnotationMirror a)
Name getName(CharSequence cs)
PackageElement getPackageElement(CharSequence name)
PackageElement getPackageOf(Element type)
TypeElement getTypeElement(CharSequence name)
boolean hides(Element hider, Element hidden)
boolean isDeprecated(Element e)
boolean isFunctionalInterface(TypeElement type)
boolean overrides(ExecutableElement overrider, ExecutableElement overridden, TypeElement type)
void printElements(Writer w, Element... elements)
用于从元素集合中选择感兴趣元素的筛选器。
static List<ExecutableElement> constructorsIn(Iterable<? extends Element> elements)
static Set<ExecutableElement> constructorsIn(Set<? extends Element> elements)
static List<VariableElement> fieldsIn(Iterable<? extends Element> elements)
static Set<VariableElement> fieldsIn(Set<? extends Element> elements)
static List<ExecutableElement> methodsIn(Iterable<? extends Element> elements)
static Set<ExecutableElement> methodsIn(Set<? extends Element> elements)
static List<PackageElement> packagesIn(Iterable<? extends Element> elements)
static Set<PackageElement> packagesIn(Set<? extends Element> elements)
static List<TypeElement> typesIn(Iterable<? extends Element> elements)
static Set<TypeElement> typesIn(Set<? extends Element> elements)
TypeMirror表示Java中的类型。这些类型包括基本类型、声明类型(类和接口类型)、数组类型、类型变量和空类型。还表示通配符类型参数、可执行文件的签名和返回类型,以及对应于包和关键字void的伪类型。
<R,P> R accept(TypeVisitor<R,P> v, P p)
TypeKind getKind()
ReferenceType表示引用类型,这些类型包括类和接口类型、数组类型、类型变量和空类型。
ArrayType表示数组类型。
TypeMirror getComponentType()//返回数组组件的类型。
表示类或接口类型。
Element asElement()//返回与此类型对应的元素
TypeMirror getEnclosingType()//返回包含该类型的类型
List<? extends TypeMirror> getTypeArguments()//返回此类型的类型变量
表示类型变量。
Element asElement()//返回与此类型变量对应的元素。
TypeMirror getLowerBound()
TypeMirror getUpperBound()
表示空类型,是null的类型。
表示无法正确建模的类或接口类型。
表示构造函数、方法或初始化器类型。
List<? extends TypeMirror> getParameterTypes()
TypeMirror getReceiverType()
TypeMirror getReturnType()
List<? extends TypeMirror> getThrownTypes()
List<? extends TypeVariable> getTypeVariables()
表示通配符类型。
TypeMirror getExtendsBound()
TypeMirror getSuperBound()
表示联合类型。在RELEASE_7源版本中,联合类型可以作为多catch异常参数的类型出现。
List<? extends TypeMirror> getAlternatives()//返回包含该联合类型的备选项。
表示泛型边界的交集类型。
List<? extends TypeMirror> getBounds()
表示基本类型。
表示在在没有合适的实际类型时使用的伪类型。如关键字void、包元素package以及Object的父类。
实现此接口的类用于在编译时类型未知时对类型进行操作。
R visit(TypeMirror t)
R visit(TypeMirror t, P p)
R visitArray(ArrayType t, P p)
R visitDeclared(DeclaredType t, P p)
R visitError(ErrorType t, P p)
R visitExecutable(ExecutableType t, P p)
R visitIntersection(IntersectionType t, P p)
R visitNoType(NoType t, P p)
R visitNull(NullType t, P p)
R visitPrimitive(PrimitiveType t, P p)
R visitTypeVariable(TypeVariable t, P p)
R visitUnion(UnionType t, P p)
R visitUnknown(TypeMirror t, P p)
R visitWildcard(WildcardType t, P p)
用于操作类型的实用方法。
Element asElement(TypeMirror t)
TypeMirror asMemberOf(DeclaredType containing, Element element)
TypeElement boxedClass(PrimitiveType p)
TypeMirror capture(TypeMirror t)
boolean contains(TypeMirror t1, TypeMirror t2)
List<? extends TypeMirror> directSupertypes(TypeMirror t)
TypeMirror erasure(TypeMirror t)
ArrayType getArrayType(TypeMirror componentType)
DeclaredType getDeclaredType(DeclaredType containing, TypeElement typeElem, TypeMirror... typeArgs)
DeclaredType getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs)
NoType getNoType(TypeKind kind)
NullType getNullType()
PrimitiveType getPrimitiveType(TypeKind kind)
WildcardType getWildcardType(TypeMirror extendsBound, TypeMirror superBound)
boolean isAssignable(TypeMirror t1, TypeMirror t2)
boolean isSameType(TypeMirror t1, TypeMirror t2)
boolean isSubsignature(ExecutableType m1, ExecutableType m2)
boolean isSubtype(TypeMirror t1, TypeMirror t2)
PrimitiveType unboxedType(TypeMirror t)
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
类。
ProcessingEnviroment
接口为注解处理器提供了许多有用的工具类。
Elements getElementUtils()
Filer getFiler()
Types getTypeUtils()
Locale getLocale()
Messager getMessager()
Map<String,String> getOptions()
SourceVersion getSourceVersion()
RoundEnvironment
接口为注解处理器提供获取当前处理循环信息的方法。
boolean errorRaised() //如果在当前循环中发生错误,则返回true
Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a)//返回使用指定注解修饰的元素
Set<? extends Element> getElementsAnnotatedWith(TypeElement a)//返回使用指定元素修饰的元素
Set<? extends Element> getRootElements()
boolean processingOver()
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提供了注解处理器报告错误消息、警告和其他通知的方式。
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表示注解的注释。
String getMessage()
String getValue()
用于封装Completion对象的工具类。
static Completion of(String value)
static Completion of(String value, String message)
代码地址如下:
https://github.com/chinesecooly/annotation-processor.git