How Lombok Generates Code at Compile Time: A Deep Dive into Annotation Processing
This article explains how Lombok uses compile‑time annotations and the Java Annotation Processing Tool (APT) to automatically generate boilerplate code such as getters, setters, and constructors, and provides step‑by‑step examples of defining custom annotations, processors, and compiling them.
Lombok is a popular Java library that reduces boilerplate by generating code through simple annotations. By adding the Lombok dependency to a Gradle project, developers can use annotations like @Data to automatically create getters, setters, constructors, and other methods during compilation.
Lombok Usage
To include Lombok in a Gradle project, add the following dependency:
compile ("org.projectlombok:lombok:1.16.6")Features
Example using @Data on a class with two fields:
@Data
public class TestLombok {
private String name;
private Integer age;
public static void main(String[] args) {
TestLombok testLombok = new TestLombok();
testLombok.setAge(12);
testLombok.setName("zs");
}
}After compilation, Lombok generates the corresponding getter and setter methods, producing a class similar to:
public class TestLombok {
private String name;
private Integer age;
public TestLombok() {}
public String getName() { return this.name; }
public Integer getAge() { return this.age; }
public void setName(String name) { this.name = name; }
public void setAge(Integer age) { this.age = age; }
}Lombok’s capabilities extend beyond @Data; many other annotations exist to simplify development.
Compile‑time Annotations
Java annotations are metadata that can be processed either at runtime (via reflection) or at compile time. Compile‑time annotations are handled by an annotation processor during the compilation phase, allowing code generation before the class files are produced.
Compile phase : The source .java files are transformed into .class files, or even into native machine code.
Runtime phase : The JVM loads the bytecode into memory and eventually unloads it after execution.
Annotation Processing Tool (APT)
APT (Annotation Processing Tool) is a compiler‑time utility that scans source files, reads annotations, and can generate additional source files, XML, or other resources. It operates on the abstract syntax tree (AST) of the code, which enables Lombok to modify the AST and inject methods.
Define a Custom Annotation
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface GeneratePrint {
String value();
}The RetentionPolicy enum has three values:
public enum RetentionPolicy {
SOURCE, // discarded by the compiler, not present in .class files
CLASS, // stored in .class files but not available at runtime
RUNTIME // retained at runtime and accessible via reflection
}The Target enum specifies where an annotation can be applied:
public enum ElementType {
TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE,
ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE
}Define an Annotation Processor
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("aboutjava.annotion.MyGetter")
public class MyGetterProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// generate source file
return true;
}
}The annotations @SupportedSourceVersion and @SupportedAnnotationTypes declare the Java version and the annotation types the processor handles. The processor overrides two key methods:
init : obtains compilation‑time environment information.
process : contains the logic that runs during compilation to generate code.
Generate Source Code in process()
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
StringBuilder builder = new StringBuilder()
.append("package aboutjava.annotion;
")
.append("public class GeneratedClass {
")
.append("\tpublic String getMessage() {
")
.append("\t\treturn \"");
for (Element element : roundEnv.getElementsAnnotatedWith(MyGetter.class)) {
String objectType = element.getSimpleName().toString();
builder.append(objectType).append(" says hello!\
");
}
builder.append("\";
")
.append("\t}
")
.append("}
");
try {
JavaFileObject source = processingEnv.getFiler().createSourceFile("aboutjava.annotion.GeneratedClass");
Writer writer = source.openWriter();
writer.write(builder.toString());
writer.flush();
writer.close();
} catch (IOException e) { }
return true;
}Test Class Using the Custom Annotation
@MyGetter
public class TestAno {
public static void main(String[] args) {
System.out.printf("1");
}
}Compilation steps:
javac aboutjava/annotion/MyGetter.java aboutjava/annotion/MyGetterProcessor.java
javac -processor aboutjava.annotion.MyGetterProcessor aboutjava/annotion/TestAno.javaDuring the second compilation, the processor runs and generates GeneratedClass.java. The generated file is displayed in the following screenshot:
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Senior Brother's Insights
A public account focused on workplace, career growth, team management, and self-improvement. The author is the writer of books including 'SpringBoot Technology Insider' and 'Drools 8 Rule Engine: Core Technology and Practice'.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
