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...
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 limitations of reflection/introspection, the discovery mechanism for annotations, and the need and means for altering compiler behaviour.
Size: 2.81 MB
Language: en
Added: Sep 20, 2024
Slides: 60 pages
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
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
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