Unravel › Java concepts › OOP and types
Custom annotations
An annotation is a label you attach to code (@Deprecated, @Test).
At a glance
Sticky notes on code.
- 1@Retention(RUNTIME) @Target(METHOD) public @interface Audited { String value() default ""; } defines the label, where it may go, and how long it stays
- 2RetentionPolicy.SOURCE notes vanish after compiling (@Override), CLASS stay in the file, RUNTIME notes can be read by reflection, which is what frameworks need
- 3A reader finds them with reflection: method.getAnnotation(Audited.class)Frameworks like Spring wrap the annotated bean in a proxy that does the extra work.
- 4Elements look like methods with optional defaults; the only allowed types are primitives, String, Class, enums, annotations and arrays of those
- An annotation that nothing reads is only documentation
- @SuppressWarnings and @Deprecated are also annotations
- RUNTIME annotations are stored in the RuntimeVisibleAnnotations attribute and the JVM creates a proxy object implementing your annotation interface when you read one
import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface Audited { String value() default ""; int level() default 1;}- line 2 uses Retention, the core of Custom annotations
- line 3 uses Target, the core of Custom annotations
Read the full explanation (about 1 min)
An annotation is a label you attach to code (@Deprecated, @Test). You can define your own with @interface; something else (the compiler, a framework, your own reflection code) then reads the labels.
A mental model
Sticky notes on code. The note itself does nothing: it only matters because somebody (a tool) walks around reading the notes and acting on them.
How it works
@Retention(RUNTIME) @Target(METHOD) public @interface Audited { String value() default ""; } defines the label, where it may go, and how long it stays.
RetentionPolicy.SOURCE notes vanish after compiling (@Override), CLASS stay in the file, RUNTIME notes can be read by reflection, which is what frameworks need.
A reader finds them with reflection: method.getAnnotation(Audited.class). Frameworks like Spring wrap the annotated bean in a proxy that does the extra work.
Elements look like methods with optional defaults; the only allowed types are primitives, String, Class, enums, annotations and arrays of those.
Going deeper
An annotation that nothing reads is only documentation. If a custom annotation seems to "not work", check the retention (must be RUNTIME), the target and whether the processing code runs for that object (a Spring proxy is not applied to calls from inside the same class).
@SuppressWarnings and @Deprecated are also annotations. Use @Deprecated(since = "2.0", forRemoval = true) to give callers a clear message.
Under the hood
RUNTIME annotations are stored in the RuntimeVisibleAnnotations attribute and the JVM creates a proxy object implementing your annotation interface when you read one. Annotation processors run at compile time and can generate code (Lombok, MapStruct).
Common mistakes
- Forgetting
@Retention(RUNTIME)so reflection sees nothing. - Expecting an annotation to change behaviour without any processor.
- Putting it on a target the
@Targetdoes not allow.
How Unravel recognises it
When you paste code, Unravel looks for these signs of custom annotations. Spotting them yourself is a good habit when you read unfamiliar code:
- Annotations:
@Retention@Target@Documented@Inherited@Repeatable@SafeVarargs@SuppressWarnings@Deprecated - Language constructs:
annotation-decl
Part of a learning path
- Object-oriented Java in depth · previous: Enums with fields and behaviour · next: Reflection
Related concepts
Builds on this
Frequently asked questions
What is Custom annotations?
An annotation is a label you attach to code (@Deprecated, @Test). You can define your own with @interface; something else (the compiler, a framework, your own reflection code) then reads the labels.
What are the most common mistakes with Custom annotations?
Forgetting @Retention(RUNTIME) so reflection sees nothing. Expecting an annotation to change behaviour without any processor. Putting it on a target the @Target does not allow.
How can I see Custom annotations in real code?
Paste your own Java into Unravel and it will point out where custom annotations appears, explain how it works at the detail level you choose, and draw the flow of the code around it.