Understanding Lombok's Annotation Processing Mechanism

Introduction

In recent projects, I've observed that many modern Java applications utilize Lombok, a library designed to automate the generation of boilerplate methods like getters, setters, and toString(). By simply annotating classes, developers can eliminate the need to manually write these repetitive methods.

Lombok Setup

Adding Dependency to pom.xml

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.2.3</version>
</dependency>

Sample POJO Class

import lombok.Getter;
import lombok.Setter;

class Person {
    @Getter
    @Setter
    private String fullName;
    
    private int yearsOld;
}

Usage Example

@Test
public void demonstrateUsage() {
    Person individual = new Person();
    individual.setFullName("John Doe");
    individual.setYearsOld(30);
    
    System.out.println(individual.getYearsOld());
    System.out.println(individual.getFullName());
}

How It Works

The above example demonstrates that even though no explicit getter or setter methods are defined in the source code, the program executes successfully. This behavior suggests that code generation occurs during compilation rather than runtime.

Annotation Types

Annotations in Java typically define their scope using @Target and retention policy via @Retention. The retention policies are:

  • SOURCE: Annotations exist only in source files
  • CLASS: Available at compile time but not at runtime
  • RUNTIME: Retained at runtime and accessible through reflection

Most custom annotations use RUNTIME for dynamic access.

Compilation Process

According to "Inside the Java Virtual Machine," the compilation process includes several stages:

  1. Preparation phase: Initializes annotation processors
  2. Resolution and symbol table population
  3. Annotation processsor execusion
  4. Code generation

Lombok leverages this mechanism by providing its own annotation processors that modify the AST (Abstract Syntax Tree) before bytecode generation.

Java SPI Mechanism

To understand how Lombok's processors are discovered, we must consider Java's Service Provider Interface (SPI). SPI allows services to be dynamically loaded at runtime using service providre configuration files.

SPI Demonstration

Define an Interface

public interface MessageService {
    void deliver();
}

Implement the Interface

public class EmailService implements MessageService {
    @Override
    public void deliver() {
        System.out.println("Sending email...");
    }
}

Register Service Provider

Create file META-INF/services/MessageService containing:

EmailService

Load and Use Services

public class ServiceProvider {
    public static void main(String[] args) {
        ServiceLoader<MessageService> loader = ServiceLoader.load(MessageService.class);
        for (MessageService service : loader) {
            service.deliver();
        }
    }
}

Lombok's Processor Registration

Lombok uses SPI to register its annotation processors. When building the jar, the necessary META-INF/services/javax.annotation.processing.Processor file is included. This file lists the fully qualified names of Lombok's processor implementations.

However, built-in JDK annotations like @Override do not rely on SPI; they are handled directly by the compiler itself.

Tags: Lombok annotation-processing java code-generation SPI

Posted on Wed, 13 May 2026 00:46:05 +0000 by rdimaggio