0Pricing
Java Academy · Lesson

Compile-Time Annotation Processors

Write an AbstractProcessor to generate source files or validate code at compile time.

Compile-Time Annotation Processors is a free Java Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Are Annotation Processors?

Annotation processors (APT — Annotation Processing Tool) run during compilation. They inspect source code annotations and can generate new source files, resource files, or raise compiler errors/warnings.

AbstractProcessor: The Base Class

Extend javax.annotation.processing.AbstractProcessor. Override process() to handle annotated elements. Declare supported annotations and Java source version.

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;

@SupportedAnnotationTypes("com.example.GenerateBuilder")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class BuilderProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations,
                           RoundEnvironment roundEnv) {
        for (Element e : roundEnv.getElementsAnnotatedWith(GenerateBuilder.class)) {
            generateBuilder((TypeElement) e);
        }
        return true;
    }
    private void generateBuilder(TypeElement cls) { /* generate source */ }
}

Registering the Processor

Register the processor in META-INF/services/javax.annotation.processing.Processor with the fully-qualified class name. Build tools (Maven, Gradle) pick it up automatically.

# META-INF/services/javax.annotation.processing.Processor
com.example.BuilderProcessor

Generating Source Files

Use processingEnv.getFiler().createSourceFile() to write a new .java file. The compiler then compiles the generated file in the same (or next) round.

JavaFileObject file = processingEnv.getFiler()
    .createSourceFile("com.example.UserBuilder");
try (Writer w = file.openWriter()) {
    w.write("package com.example;\n");
    w.write("public class UserBuilder {\n");
    w.write("  // generated builder\n");
    w.write("}\n");
}

Inspecting the Element Model

Annotation processors work with Elements (not Classes — source is not yet compiled). Use TypeElement, VariableElement, and ExecutableElement.

TypeElement classElement = (TypeElement) element;
String className = classElement.getSimpleName().toString();
List<VariableElement> fields = classElement.getEnclosedElements().stream()
    .filter(e -> e.getKind() == ElementKind.FIELD)
    .map(e -> (VariableElement) e)
    .collect(Collectors.toList());

Emitting Compiler Errors

Call processingEnv.getMessager().printMessage() to emit errors, warnings, or notes. This is how custom constraint annotations signal violations at compile time.

processingEnv.getMessager().printMessage(
    Diagnostic.Kind.ERROR,
    "@GenerateBuilder requires at least one field",
    element
);

Multi-Round Processing

Annotation processing happens in rounds. If you generate a file that contains annotations, the processor may run again on the next round. roundEnv.processingOver() signals the final round.

if (roundEnv.processingOver()) {
    // last round — write summary files or do cleanup
}

Lombok Under the Hood

Lombok uses a non-standard APT extension that modifies the AST of existing classes (not allowed by the spec). Its annotations like @Data, @Builder, and @Getter generate methods directly into the compiled class.

MapStruct Code Generation

MapStruct generates type-safe mapper implementations at compile time. It reads @Mapper-annotated interfaces and writes implementations as regular Java classes — zero reflection at runtime.

@Mapper
public interface UserMapper {
    UserDto toDto(User user);
    User toEntity(UserDto dto);
}
// MapStruct generates: UserMapperImpl.java at compile time

Testing Annotation Processors

Use google/compile-testing or Kapt (for Kotlin) to unit-test processors. Feed source strings and assert generated files or compiler errors.

JavaFileObject src = JavaFileObjects.forSourceString("Test",
    "@GenerateBuilder public class Test { private String name; }");
Compilation c = Compiler.javac().withProcessors(new BuilderProcessor()).compile(src);
CompilationSubject.assertThat(c).succeeded();
CompilationSubject.assertThat(c).generatedSourceFile("TestBuilder");

Processor Packaging

Package the processor in a separate Maven module. The annotation module (annotations only) is a compile dependency; the processor module is annotationProcessor scope, never a runtime dependency.

// build.gradle:
dependencies {
    implementation "com.example:my-annotations:1.0"
    annotationProcessor "com.example:my-processor:1.0"
}

Quick Check

Where do you register an annotation processor for automatic discovery?

Recap

Extend AbstractProcessor, register in META-INF/services, generate source with Filer, emit errors with Messager. Package processors separately. Use compile-testing for unit tests. Eliminates reflection overhead at runtime.

Frequently asked questions

Is the “Compile-Time Annotation Processors” lesson free?

Yes — the full text of “Compile-Time Annotation Processors” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.

What will I learn in “Compile-Time Annotation Processors”?

Write an AbstractProcessor to generate source files or validate code at compile time. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Java Academy?

No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Compile-Time Annotation Processors” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Java Academy lesson?

Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Defining Annotations: Elements and Defaults
  2. Retention Policies and Target Types
  3. Runtime Annotation Processing
  4. Compile-Time Annotation Processors
← Back to Java Academy