Detailed explanation of Java annotation and annotation processor

Posted by gasper000 on Wed, 26 Jan 2022 19:09:18 +0100

preface

Annotations are labels that are inserted into the source file. Javac will generate the same bytecode for the code with or without annotations. If you want to use annotations, you must provide annotation processing tools. These tools can process annotations at run time (reflection), at compile time (language model) or at bytecode level (bytecode Engineering). This article will only describe the tools for processing annotations at compile time, that is, language model and annotation processor. If you want to use annotation processor, you must understand language model API and compiler API. The above concepts mentioned in this article are recorded in detail in other articles of the author, which can be consulted if you are interested.

annotation

Annotation interface

All annotations are defined through the annotation interface, and all annotation interfaces are implicitly extended from Java lang.annotation. Annotation interface. The annotation interface does not support inheritance and does not support circular dependency (annotation B cannot use annotation A if it uses itself or annotation A uses annotation b).

modifiers @interface AnnotationName{
	type elementName1();
	type elementName2() default value;
} 

All annotations are used in the following forms. If the annotation element has a default value, it can be used without specifying a value for the element.

@AnnotationName(elementName1=value1,eementName2=value2)

Annotation element

All elements defined in the annotation interface are called annotation elements. Annotation elements must be of the following types:

  • Basic type
  • String
  • Class
  • enum
  • Annotation
  • An array of the above types

Default value

The value of the annotation element cannot be null. You can use default to specify a default value for the annotation element:

@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 {};
}

The default value is not stored with the annotation, but obtained dynamically. If you modify the default value of the annotation, the new default value will also be used in the compiled class file.

marker annotation

If there are no elements in an annotation or all elements have default values, this annotation is called tag annotation, which can be used without parentheses.

@AnnotationName

Single value annotation

If an element named value is defined in the annotation and value is the only element that needs to be assigned when using the annotation, the value of value can be given directly in parentheses.

@AnnotationName(value)

Meta annotation

Meta annotations are annotations for annotations.

@Target

@Target is used to limit where the current annotation can be used. An annotation not constrained by @ target can be used anywhere.

public @interface Target {
    ElementType[] value();
}
Attribute valueexplain
ANNOTATION_TYPECan be used in annotations
CONSTRUCTORCan be used on constructors
FIELDCan be used on fields
LOCAL_VARIABLECan be used on local variables
METHODIt can be used in methods
PACKAGEIt can be used on bags
PARAMETERCan be used on method parameters
TYPECan be used on classes and interfaces
TYPE_PARAMETERCan be used on generic parameters
TYPE_USEType usage

When an annotation is used on a local variable, it can only be processed at run time. It should be that the class file does not record the local variable information.

@Retention

@Retention is used to specify how long an annotation should be retained. The default value is CLASS.

public @interface Retention {
    RetentionPolicy value();
}
Attribute valueexplain
SOURCEKeep until compilation
CLASSKeep until run
RUNTIMEKeep after running

@Documented

@Documented is used to prompt the archiving tool whether to include this annotation when archiving.

public @interface Documented {
}

@Inherited

@Inherited can only be applied to the annotation annotated by @ Target(ElementType.TYPE). The function of this annotation is to allow subclasses to inherit the annotation annotated by this annotation when inheriting the parent class of the annotation annotated by this annotation.

public @interface Inherited {
}

@Repeatable

@Repeatable is used to specify that the current annotation can be used multiple times in the same place.

public @interface Repeatable {
    Class<? extends Annotation> value();
}

Its value element specifies a container annotation of the current annotation, and the storage time of this container annotation must be greater than or equal to the sub annotation:

@Repeatable(MyAnnotations.class)
@interface MyAnnotation{   
}

@interface MyAnnotations{
    MyAnnotation[] value();
}

annotation processor


Processor represents the interface of the annotation processor. The annotation processor can process annotations at compile time. Because the annotation processing function is integrated into Javac, the annotation processor can be called to process annotations at compile time through the following commands:

javac -processor <processorClassNames> <src>

The compiler locates the annotations in the source file, and each annotation processor executes them in turn to get the annotations they are interested in. The annotation processor can only create source files and cannot modify existing source files. If an annotation processor creates a new source file, the above process will be repeated. If a cycle does not produce any new source files, all source files will be compiled.

void init(ProcessingEnvironment processingEnv)//This method will be called by the annotation processing tool and enter the processingenvironment parameter.
boolean	process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)//The method that actually handles annotations returns true after all processing is completed
Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText)
Set<String>	getSupportedAnnotationTypes()//Specifies the annotations that the annotation processor can process
Set<String>	getSupportedOptions()//Specifies the options supported by the annotation processor. Each string returned in the collection must be a sequence of identifiers separated by periods.
SourceVersion getSupportedSourceVersion()//Specifies the Java version of the source code

The last three methods above can be replaced by the following three annotations:

annotation
@SupportedAnnotationTypes
@SupportedOptions
@SupportedSourceVersion

When the bedstead annotation processor, it usually inherits the AbstractProcessor class.

RoundEnvironment

The annotation processing framework provides an object that implements the interface for the annotation handler, so that the handler can query information about a round of annotation processing.

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

Processingenvironment provides many useful tool classes Elements, Types and Filer. So that handlers can use tools to write new files, report error messages, and find other utilities.

Elements getElementUtils()
Filer getFiler()
Types getTypeUtils()
Locale getLocale()
Messager getMessager()
Map<String,String> getOptions()
SourceVersion getSourceVersion()

Filer

Filer supports the annotation processor to create new files. Three kinds of files can be distinguished: source file, class file and auxiliary resource file.

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

The Messager provides a way for the annotation processor to report error messages, warnings, and other notifications.

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 indicates the annotation of the annotation.

String	getMessage()
String	getValue()

Completions

Tool class used to encapsulate the Completion object.

static Completion of(String value)
static Completion of(String value, String message)

demo

The code address is as follows:

https://github.com/chinesecooly/annotation-processor.git

Topics: Java Back-end Annotation