Annotation Processing

About

Annotation Processing is a powerful feature in Java that allows developers to intercept and process annotations at compile time. It plays a vital role in code generation, validation, and automation of boilerplate tasks in large-scale applications and frameworks like Spring, Lombok, Dagger, etc.

Annotation processing is a compile-time mechanism that inspects the source code for annotations and can generate additional source files, perform validations, or create metadata.

It is handled by tools that implement the javax.annotation.processing.Processor interface (now jakarta.annotation.processing.Processor in Jakarta EE) and is integrated with the Java compiler (javac).

Annotation processors run during compilation and do not affect runtime behavior directly, but the code they generate or validate influences the final application.

When Is Annotation Processing Used ?

Annotation processing is used when:

  • We want to generate source code automatically (e.g., builder classes, DTOs, factories).

  • We want to validate annotation usage (e.g., checking constraints).

  • We want to generate configuration metadata (e.g., Spring factories, JSON schema, etc.).

  • We are building a framework or library that relies on compile-time structure.

Examples

Use Case
Tools/Frameworks

Generate boilerplate code

Lombok

Dependency Injection

Dagger, Hilt

JSON Serialization

AutoValue, Gson, Jackson

Configuration Metadata

Spring Boot

Mapper Generation

MapStruct

How It Works ?

  1. We create a custom annotation.

  2. We implement an annotation processor that:

    • Identifies target annotations.

    • Uses the compiler’s Abstract Syntax Tree (AST) or Elements/Mirrors API.

    • Optionally generates new source files.

  3. The annotation processor is registered using META-INF/services/javax.annotation.processing.Processor file.

  4. During compilation, javac invokes the processor, processes annotations, and generates code before final compilation completes.

Use Case: @AutoToString Annotation

We’ll create a custom annotation @AutoToString that, when applied to a class, generates a helper class with a toString() method implementation for that class.

This is a compile-time code generation example - the processor will generate code based on our annotation.

Folder Structure

Define the Annotation

  • @Target(TYPE) — used on classes

  • @Retention(SOURCE) — we only care during compile time

Implement the Annotation Processor

  • It identifies all classes annotated with @AutoToString.

  • For each class, it generates a helper class like PersonToStringGenerator.java.

  • It creates a static method that builds a toString output using the class fields.

Register the Processor

Path: src/main/resources/META-INF/services/javax.annotation.processing.Processor Contents:

This tells the compiler which class is an annotation processor.

Use the Annotation in a Client Class

When this is compiled, the processor generates:

Use the Generated Code

Build the JAR using Maven

1. Add pom.xml

Here is a basic Maven config

No dependencies are needed unless we use external libraries in our processor.

2. Package the JAR

Run this from inside our project folder

This will generate a JAR in

That JAR will contain

  • Our processor class

  • The @AutoToString annotation

  • The META-INF/services/javax.annotation.processing.Processor file

Use the JAR in Another Project

Now in our actual project (say, where Person.java is):

  1. Put the above JAR in the classpath

  2. Compile with:

This will trigger our annotation processor during compilation.

Use the JAR in Maven Project

In our main project's pom.xml, we can declare the processor as a build-time dependency only:

And for automatic annotation processing:

Last updated