Java Annotation Processor

From NovaOrdis Knowledge Base
Jump to navigation Jump to search

External

Internal

Overview

The Pluggable Annotation Processing API is specified by JSR 269 and can be used to develop custom annotation processors. Annotation processing is actively used in many Java libraries, for instance to generate metaclasses in JPA or to augment classes with boilerplate code in Lombok library.

The processing API can only be used to generate new files, not change the existing ones. Lombok is an exception.

Annotation Processing Process

Unless annotation processing is disabled on javac with the -proc:none option, the compiler searches for any annotation processors that are available. The search path can be specified with the -processorpath option. If the option is not given, the user class path is used. Processors are located by means of service provider-configuration files named META-INF/services/javax.annotation.processing.Processor on the search path. Such files should contain the names of any annotation processors to be used, listed one per line. Alternatively, processors can be specified explicitly, using the -processor option.

After scanning the source files and classes on the command line to determine what annotations are present, the compiler queries the processors to determine what annotations they process. When a match is found, the processor will be invoked. A processor may "claim" the annotations it processes, in which case no further attempt is made to find any processors for those annotations. Once all annotations have been claimed, the compiler does not look for additional processors.

If any processors generate any new source files, another round of annotation processing will occur: any newly generated source files will be scanned, and the annotations processed as before. Any processors invoked on previous rounds will also be invoked on all subsequent rounds. This continues until no new source files are generated.

After a round occurs where no new source files are generated, the annotation processors will be invoked one last time, to give them a chance to complete any work they may need to do. Finally, unless the -proc:only option is used, the compiler will compile the original and all the generated source files.

Programming Model

Playground Example

Playground Annotation Processor Example

Implement a Processor

@SupportedAnnotationTypes("playground.ap.Blue")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class BlueProcessor extends AbstractProcessor {

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

  //
  // called by the compiler for every source file containing matching annotations. The annotations
  // are passed as Set<? extends TypeElement>, and the information about the current processing
  // round is passed as roundEnv. The method should return true if the annotation processor has
  // processed all passed annotations.
  //

  for(TypeElement annotation: annotations) {

      // these are the elements annotated with @Blue

      Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation);

      for(Element e: annotatedElements) {

          //
          // select only annotated fields
          //

          ElementKind kind = e.getKind();

          if (ElementKind.FIELD.equals(kind)) {

              Name name = e.getSimpleName();
              String s = name.toString();
              System.out.println("name: " + s);
           }
        }
    }

    return false;
  }
}

Processing environment providing by the tool framework is available as AbstractProcessor's processingEnv protected field.

Provide the Descriptor

META-INF/services/javax.annotation.processing.Processor should contain the fully qualified class name of the processor. If there are multiple processors, they should specified one per line:

playground.ap.BlueProcessor

Build the JAR

The JAR should contain the processor class and the descriptor.

Using an Annotation Processor

Playground Example of Annotation Processor User

If the JAR containing the annotation processor also contains the correct descriptor, simply placing the JAR in the compiler's user classpath will make the processor available for annotation processing, according to the sequence described in Annotation Processing Process.