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 filesCLASS: Available at compile time but not at runtimeRUNTIME: 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:
- Preparation phase: Initializes annotation processors
- Resolution and symbol table population
- Annotation processsor execusion
- 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.