Custom Annotations in Java with Project Lombok

yanngaelgueheneuc 119 views 60 slides Sep 20, 2024
Slide 1
Slide 1 of 60
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34
Slide 35
35
Slide 36
36
Slide 37
37
Slide 38
38
Slide 39
39
Slide 40
40
Slide 41
41
Slide 42
42
Slide 43
43
Slide 44
44
Slide 45
45
Slide 46
46
Slide 47
47
Slide 48
48
Slide 49
49
Slide 50
50
Slide 51
51
Slide 52
52
Slide 53
53
Slide 54
54
Slide 55
55
Slide 56
56
Slide 57
57
Slide 58
58
Slide 59
59
Slide 60
60

About This Presentation

Annotations are a kind of metadata, which developers can use to change the behaviour of the compiler or the runtime beyond what's possible in the programming language itself. This presentation shows how to create a custom annotation @Singleton, in Java, using Project Lombok. It discusses the lim...


Slide Content

Yann-Gaël Guéhéneuc
(/jan/, he/il)
Work licensed under Creative Commons
BY-NC-SA 4.0 International
Custom Annotations
in Java with
Project Lombok
[email protected]
Version 1.0
2024/04/15

2/60
Problem: How to annotate code constituents
with metadata to guide compilation, execution?
Solution: Provide ad-hoc annotations

3/60
Ad-hoc Annotations 
Modifiers

Javadoc tags

Built-in annotations
Problem: How to annotate code constituents
with metadata to guide compilation, execution?
Solution: Provide ad-hoc annotations

4/60
Ad-hoc Annotations 
Modifiers
–Since JDK v1.0 (1996)
–Examples:
transient
,
volatile

Javadoc tags

Built-in annotations
Problem: How to annotate code constituents
with metadata to guide compilation, execution?
Solution: Provide ad-hoc annotations

5/60
Ad-hoc Annotations 
Modifiers

Javadoc tags
–Since JDK v1.0
–Examples:
@Deprecated
,
@throws

Built-in annotations
Problem: How to annotate code constituents
with metadata to guide compilation, execution?
Solution: Provide ad-hoc annotations

6/60
Ad-hoc Annotations 
Modifiers

Javadoc tags

Built-in annotations
–Since JDK v1.5 (2004)
–Example:
@Override
,
@interface
Problem: How to annotate code constituents
with metadata to guide compilation, execution?
Solution: Provide ad-hoc annotations

7/60
Kinds of Annotations
https://www.geeksforgeeks.org/annotations-in-java/

8/60
Kinds of Annotations 
Marker annotations

Value annotations

Meta annotations
public class
TestSingleton {
@Test public void
test() {
final
MySingleton
msc
= MySingleton.getInstance();
System.
out
.println(
msc
);
Assert.assertNotNull(
msc
);
}
}

9/60
Kinds of Annotations 
Marker annotations

Value annotations

Meta annotations
public class
TestSingleton {
@Test public void
test() {
final
MySingleton
msc
= MySingleton.getInstance();
System.
out
.println(
msc
);
Assert.assertNotNull(
msc
);
}
}
Declared by JUnit

10/60
Kinds of Annotations 
Marker annotations

Value annotations

Meta annotations
@MetaInfServices
(JavacAnnotationHandler.
class
)
public class
SingletonHandler
extends
JavacAnnotationHandler<
...
> {
public void
handle(
final
AnnotationValues<
...
>
annotation
,
final
JCTree.JCAnnotation
ast
,
final
JavacNode
annotationNode
) {
...

11/60
Kinds of Annotations 
Marker annotations

Value annotations

Meta annotations
@MetaInfServices
(JavacAnnotationHandler.
class
)
public class
SingletonHandler
extends
JavacAnnotationHandler<
...
> {
public void
handle(
final
AnnotationValues<
...
>
annotation
,
final
JCTree.JCAnnotation
ast
,
final
JavacNode
annotationNode
) {
...
One value

12/60
Kinds of Annotations 
Marker annotations

Value annotations

Meta annotations
@Target
(ElementType.
TYPE
)
@Retention
(RetentionPolicy.
RUNTIME
)
@Inherited
@Documented
public @interface
Singleton
{
}

13/60
Kinds of Annotations 
Marker annotations

Value annotations

Meta annotations
@Target
(ElementType.
TYPE
)
@Retention
(RetentionPolicy.
RUNTIME
)
@Inherited
@Documented
public @interface
Singleton
{
}
Meta-data on
an annotation

14/60
Problem: How to annotate code constituents with
metadata to guide compilation, execution?
Solution: Provide general annotation mechanisms

15/60
Annotation Mechanisms 
Reflection (i.e., introspection)

Truly general mechanism
Problem: How to annotate code constituents with
metadata to guide compilation, execution?
Solution: Provide general annotation mechanisms

16/60
Annotation Mechanisms 
Reflection (i.e., introspection)
–Since JDK v1.5 (2004)
–Example:
java.lang.reflect.Method.
getAnnotation(Class<T> annotationClass)

Truly general mechanism
Problem: How to annotate code constituents with
metadata to guide compilation, execution?
Solution: Provide general annotation mechanisms

17/60
Annotation Mechanisms 
Reflection (i.e., introspection)

Truly general mechanism
–Since JDK v1.6 (2006)
– javax.annotation.processing.Processor
Problem: How to annotate code constituents with
metadata to guide compilation, execution?
Solution: Provide general annotation mechanisms

18/60
Annotation Mechanisms 
Both introspection and Processorcan
only readannotations (and their value)
and, possibly, generatenew files
https://www.baeldung.com/java-annotation-processing-build er

19/60
Annotation Mechanisms 
Reflection (i.e., introspection)
–JUnit v4+
–Since 2006
public class
TestSingleton {
@Test public void
test() {
final
MySingleton
msc
= MySingleton.getInstance();
System.
out
.println(
msc
);
Assert.assertNotNull(
msc
);
}
}

20/60
Annotation Mechanisms 
Reflection (i.e., introspection)
–JUnit v4+
–Since 2006
public class
TestClass
implements
Annotatable {
protected void
scanAnnotatedMembers(
Map<Class<...>, List<...>>
methodsForAnnotations
,
Map<Class<...>, List<...>>
fieldsForAnnotations
) {
for
(Class<?>
eachClass
: getSuperClasses(
clazz
)) {
for
(Method
eachMethod
: ...(
eachClass
)) {
// Simplified! for
(Annotation
each
:
eachMethod
.getAnnotations()) {
// ...

21/60
Annotation Mechanisms 
Reflection (i.e., introspection)
–JUnit v4+
–Since 2006
public class
TestClass
implements
Annotatable {
protected void
scanAnnotatedMembers(
Map<Class<...>, List<...>>
methodsForAnnotations
,
Map<Class<...>, List<...>>
fieldsForAnnotations
) {
for
(Class<?>
eachClass
: getSuperClasses(
clazz
)) {
for
(Method
eachMethod
: ...(
eachClass
)) {
// Simplified! for
(Annotation
each
:
eachMethod
.getAnnotations()) {
// ...
Provided by the JDK

22/60
Annotation Mechanisms 
Truly general mechanism
–The result is not a function of the presence or
absence of other inputs (orthogonality)
–Processing the same input produces the same
output (consistency)
–Processing input A followed by B is equivalent to
processing B then A (commutativity)
–Processing an input does not rely on the output
of other processors (independence)
https://docs.oracle.com/javase/8/docs/api/javax/annotation/pr ocessing/Processor.html

23/60
Annotation Mechanisms 
Truly general mechanism
–The result is not a function of the presence or
absence of other inputs (
orthogonality
)
–Processing the same input produces the same
output (
consistency
)
–Processing input A followed by B is equivalent to
processing B then A (
commutativity
)
–Processing an input does not rely on the output
of other processors (
independence
)
https://docs.oracle.com/javase/8/docs/api/javax/annotation/pr ocessing/Processor.html

24/60
Annotation Mechanisms 
Truly general mechanism
https://www.baeldung.com/java-annotation-processing-build er
@SupportedAnnotationTypes
(
"....BuilderProperty"
)
@SupportedSourceVersion
(SourceVersion.
RELEASE_8
)
public class
BuilderProcessor
extends
AbstractProcessor {
@Override public boolean
process(
final
Set<?
extends
TypeElement>
annotations
,
final
RoundEnvironment
roundEnv
) {
return false
;
}
}

25/60
Annotation Mechanisms 
Truly general mechanism
https://www.baeldung.com/java-annotation-processing-build er
@SupportedAnnotationTypes
(
"....BuilderProperty"
)
@SupportedSourceVersion
(SourceVersion.
RELEASE_8
)
public class
BuilderProcessor
extends
AbstractProcessor {
@Override public boolean
process(
final
Set<?
extends
TypeElement>
annotations
,
final
RoundEnvironment
roundEnv
) {
return false
;
}
}
Provided by the JDK,
implements Processor

26/60
Annotation Mechanisms 
Truly general mechanism
https://www.baeldung.com/java-annotation-processing-build er
private void
writeBuilderFile(String
className
,
Map<String, String>
setterMap
)
throws
IOException {
String
packageName
=
null
;
int
lastDot
=
className
.lastIndexOf(
'.’
);
if
(
lastDot
> 0) {
...
}
String
simpleClassName
=
className
.substring(
lastDot
+ 1);
String
builderClassName
=
className
+
"Builder"
;
String
builderSimpleClassName
=
builderClassName.
...;
JavaFileObject
builderFile
= processingEnv.getFiler()
.createSourceFile(
builderClassName
);
try
(PrintWriter
out
=
new
PrintWriter(
builderFile
....())) {
// ...

27/60
Problem: How does the compiler know what
annotation processors exist?
Solution: Provide general discovery mechanisms

28/60

Annotation processor tool

Compiler argument

Processor JAR

Auto-service Library

Maven
Problem: How does the compiler know what
annotation processors exist?
Solution: Provide general discovery mechanisms
Discovery Mechanisms

29/60

Annotation processor tool

Compiler argument

Processor JAR

Auto-service Library

Maven
Problem: How does the compiler know what
annotation processors exist?
Solution: Provide general discovery mechanisms
Discovery Mechanisms

30/60
Discovery Mechanisms 
Compiler argument
javac -processor package1.Processor1,package2.Processor2 SourceFile.java
https://www.baeldung.com/java-annotation-processing-build er

31/60
Discovery Mechanisms 
Compiler argument
javac -processor package1.Processor1,package2.Processor2 SourceFile.java
javac -processor com.baeldung.annotation.processor.MyProcessor Person.java
https://www.baeldung.com/java-annotation-processing-build er

32/60
Discovery Mechanisms 
Processor JAR

Auto-service Library
META-INF/services/javax.annotation.processing.Processor @
AutoService(Processor.
class
)
public
BuilderProcessor
extends
AbstractProcessor {
// ...
}

33/60
Discovery Mechanisms 
Processor JAR

Auto-service Library
META-INF/services/javax.annotation.processing.Processor @
AutoService(Processor.
class
)
public
BuilderProcessor
extends
AbstractProcessor {
// ...
}

34/60
Discovery Mechanisms 
Processor JAR

Auto-service Library
META-INF/services/javax.annotation.processing.Processor @
AutoService(Processor.
class
)
public
BuilderProcessor
extends
AbstractProcessor {
// ...
}
The “magic” happens (in) here

35/60
Problem: How to modify code constituents using
annotation during compilation?
Solution: Hook into the compiler and hack its AST

36/60
https://www.southernexposure.com/products/lombok-chile-lombak-hot-pepper/

37/60
Caveats 
Only Lombok v1.14.8

Only JDK v1.8 of 2014
–Until JDK v1.9 in 2017
• Why the gap?
• Because of modularisation!

38/60
Caveats 
Only Lombok v1.14.8

Only JDK v1.8 of 2014
–Until JDK v1.9 in 2017
• Why the gap?
• Because of modularisation!

39/60
Caveats 
Only Lombok v1.14.8

Only JDK v1.8 of 2014
–Until JDK v1.9 in 2017
• Why the gap?
• Because of modularisation!
Don’t try with Lombok 1.18+
Don’t even try with JDK 22!

40/60
Singleton Design Pattern 
Of course
public class
AnotherSingleton {
private static class
AnotherSingletonHolder {
private static
AnotherSingleton
UNIQUE_INSTANCE
=
new
AnotherSingleton();
}
public static
AnotherSingleton getInstance() {
return
AnotherSingletonHolder.
UNIQUE_INSTANCE
;
}
private
AnotherSingleton() {
}
// Some other methods
}
https://www.baeldung.com/lombok-custom-annotation

41/60
Singleton Design Pattern 
Of course
public class
AnotherSingleton {
private static class
AnotherSingletonHolder {
private static
AnotherSingleton
UNIQUE_INSTANCE
=
new
AnotherSingleton();
}
public static
AnotherSingleton getInstance() {
return
AnotherSingletonHolder.
UNIQUE_INSTANCE
;
}
private
AnotherSingleton() {
}
// Some other methods
}
What happens
with this class?
https://www.baeldung.com/lombok-custom-annotation

42/60
Singleton Design Pattern 
Much simpler
@Singleton public class
MySingleton {
// Some other methods
}

43/60
Several Steps 1.
Create @Singletonannotation
2.
Create JavacAnnotationHandler
–Or
EclipseAnnotationHandler
3.
Compile handler
4.
Register handler
5.
Compile classes that use singletons
6.
Run the tests or the program!
https://projectlombok.org/contributing/lombok-execution- path

44/60
Several Steps 1.
Create @Singletonannotation
2.
Create JavacAnnotationHandler
–Or
EclipseAnnotationHandler
3.
Compile handler
4.
Register handler
5.
Compile classes that use singletons
6.
Run the tests or the program!
Provided by the Lombok library
https://projectlombok.org/contributing/lombok-execution- path

45/60
Step 1
@Target
(ElementType.
TYPE
)
@Retention
(RetentionPolicy.
RUNTIME
)
@Inherited
@Documented
public @interface
Singleton
{
}
@Singleton public class
MySingleton {
}
public class
TestSingleton {
@Test public void
test() {
final
MySingleton
msc
= MySingleton.getInstance();
System.
out
.println(
msc
);
Assert.assertNotNull(
msc
);
}
}

46/60
Step 1
@Target
(ElementType.
TYPE
)
@Retention
(RetentionPolicy.
RUNTIME
)
@Inherited
@Documented
public @interface
Singleton
{
}
@Singleton public class
MySingleton {
}
public class
TestSingleton {
@Test public void
test() {
final
MySingleton
msc
= MySingleton.getInstance();
System.
out
.println(
msc
);
Assert.assertNotNull(
msc
);
}
}
What happens
with this call?

47/60
Step 1
@Target
(ElementType.
TYPE
)
@Retention
(RetentionPolicy.
RUNTIME
)
@Inherited
@Documented
public @interface
Singleton
{
}
@Singleton public class
MySingleton {
}
public class
TestSingleton {
@Test public void
test() {
final
MySingleton
msc
= MySingleton.getInstance();
System.
out
.println(
msc
);
Assert.assertNotNull(
msc
);
}
}
The method getInstance()
is undefined for the type
MySingleton

48/60
Step 2

49/60
Step 3 
Without full paths, simplified
javac.exe
-dtarget/classes
-classpathtarget/classes;.m2/repository/com/sun/tools/tools/1.8/
tools-1.8.jar;.m2/repository/org/projectlombok/lombok/1.14.8/
lombok-1.14.8.jar;.m2/repository/org/kohsuke/metainf-services/
metainf-services/1.8/metainf-services-1.8.jar
src/main/java/.../singleton/example/MySingleton.java
src/main/java/.../singleton/Singleton.java
src/main/java/.../singleton/javac/SingletonHandler.java

50/60
Step 3

51/60
Step 4 
Done automatically by Lombok
Note: Writing META-INF/services/lombok.javac.JavacAnnotationHandler

52/60
Step 5 
Without full paths, simplified
javac.exe
-dtarget/classes
-classpathtarget/classes;.m2/repository/org/projectlombok/lombok/
1.14.8/lombok-1.14.8.jar;.m2/repository/junit/junit/4.13.2/junit-
4.13.2.jar
src/main/java/.../singleton/example/MySingleton.java
src/test/java/.../singleton/test/TestSingleton.java

53/60
Step 5

54/60
Step 6 
Without full paths, simplified
java.exe –classpathtarget/classes;.m2/repository/org/hamcrest/
hamcrest-core/1.3/hamcrest-core-1.3.jar;.m2/repository/junit/junit/
4.13.2/junit-4.13.2.jar
org.junit.runner.JUnitCore
net.ptidej.tutorial.lombok.singleton.test.TestSingleton

55/60
Step 6 
Without full paths, simplified
java.exe –classpathtarget/classes;.m2/repository/org/hamcrest/
hamcrest-core/1.3/hamcrest-core-1.3.jar;.m2/repository/junit/junit/
4.13.2/junit-4.13.2.jar
org.junit.runner.JUnitCore
net.ptidej.tutorial.lombok.singleton.test.TestSingleton
JUnit version 4.13.2
.net.ptidej.tutorial.lombok.singleton.example.MySingleton@4d76f3f8
Time: 0.029
OK (1 test)

56/60
Conclusion 
Annotations
– Meta-data

Code generation

Code transformation

Other considerations
– Aspect Oriented
Programming
– Spring Boot
Future work?

57/60
Conclusion 
Annotations
– Meta-data

Code generation

Code transformation

Other considerations
– Aspect Oriented
Programming
– Spring Boot
Future work?

58/60
Conclusion 
Annotations
– Meta-data

Code generation

Code transformation

Other considerations
– Aspect Oriented
Programming
– Spring Boot
Future work?

59/60

60/60
Image Credits 
https://levelup.gitconnected.com/lombok-to -use-or-
not-to-use-that-is-the-blog-e7a3a8f97e8f

https://forums.sherdog.com/threads/best-
simpsons-episode-ever.3855795/

https://tenor.com/en-GB/search/homer-simpson-
with-hair-gifs

https://www.facebook.com/eagertolearnJava/