Working with Annotation Processing

1. Understanding Annotation Processor Lifecycle

javac → discover processors (META-INF/services) → init()
   → for each round: process(annotations, RoundEnv)
   → generate sources → re-run with new sources → ...
   → final round (processingOver()==true)
      
PhaseHook
Initinit(ProcessingEnvironment)
Per roundprocess()
SupportedgetSupportedAnnotationTypes()
Source levelgetSupportedSourceVersion()

2. Creating AbstractProcessor Subclass

AnnotationEffect
@SupportedAnnotationTypesDeclarative supported types
@SupportedSourceVersionJava version
@SupportedOptionsCompiler -A options

Example: Skeleton

@SupportedAnnotationTypes("com.acme.Audit")
@SupportedSourceVersion(SourceVersion.RELEASE_21)
public class AuditProcessor extends AbstractProcessor {
    @Override public boolean process(Set<? extends TypeElement> ann, RoundEnvironment env) {
        for (Element e : env.getElementsAnnotatedWith(Audit.class)) { /* ... */ }
        return true;
    }
}

3. Using ProcessingEnvironment

ServiceUse
getElementUtils()Element queries
getTypeUtils()Type relations (subtype, erasure)
getFiler()Generate files
getMessager()Report errors/warnings
getOptions()-A key=value

4. Accessing RoundEnvironment

MethodReturns
getElementsAnnotatedWith(A)Set<Element>
getRootElements()Top-level types this round
processingOver()Last round?
errorRaised()Compilation has error

5. Generating Source Files (Filer)

MethodOutput
createSourceFile(name).java file
createClassFile(name).class file
createResource(loc, pkg, file)Arbitrary resource

6. Using JavaPoet for Code Generation

BuilderUse
TypeSpec.classBuilder("X")Class
MethodSpec.methodBuilder("m")Method
FieldSpec.builder(T, "f")Field
JavaFile.builder(pkg, type).build()Compilation unit
$T, $L, $S, $NType, literal, string, name placeholders

Example: Generate hello class

TypeSpec hello = TypeSpec.classBuilder("Hello")
    .addModifiers(Modifier.PUBLIC)
    .addMethod(MethodSpec.methodBuilder("greet")
        .returns(String.class)
        .addStatement("return $S", "hi")
        .build())
    .build();
JavaFile.builder("gen", hello).build().writeTo(filer);

7. Handling Compilation Errors (Messager)

KindEffect
ERRORFails compilation
WARNING / MANDATORY_WARNINGVisible warning
NOTEInfo only
Element-anchoredPass Element for IDE jump

8. Configuring Annotation Processors

Build ToolMechanism
Mavenmaven-compiler-plugin <annotationProcessorPaths>
GradleannotationProcessor "group:artifact"
javac-processor com.x.P -Akey=val

9. Using Service Provider Interface

MechanismDetail
FileMETA-INF/services/javax.annotation.processing.Processor
ContentFQN of processor (one per line)
AutoUse Google AutoService @AutoService

10. Debugging Annotation Processors

ApproachSetup
Remote debug-J-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005
IDERun javac as test in IDE; attach debugger
Log via MessagerNOTE level prints to compile output

11. Understanding Incremental Processing

ToolMode
Gradleisolating (one input → outputs) or aggregating
DeclarationResource META-INF/gradle/incremental.annotation.processors
BenefitSkip processor for unchanged inputs
PitfallAggregating processors invalidate widely