Annotations and Reflection
Annotations as metadata, custom annotations, SOURCE/CLASS/RUNTIME retention, @Target, reading annotations on classes, methods, fields and constructors, and how Spring, JUnit and Hibernate use them.
#Introduction
In previous chapters we learned how Reflection inspects classes, constructors, fields and methods. But modern frameworks like Spring, Hibernate, JUnit, Jackson and Mockito mainly depend on annotations.
- How does Spring know that a class is a service? →
@Service public class UserService {} - How does JUnit know which method is a test? →
@Test public void loginTest() {} - How does Hibernate know which class is an entity? →
@Entity public class Employee {}
Answer: frameworks read annotations at runtime, through Reflection. Without it, annotation-based programming as we know it wouldn't exist.
#What is an Annotation?
An annotation is metadata attached to Java program elements. It provides additional information to the compiler, the JVM, frameworks and tools. Annotations don't change program logic directly — they provide information.
@Override
public String toString() {
return "Employee";
}Here @Override is an annotation. Another example — Spring reads @Service using Reflection:
@Service
public class UserService {
}Real-Life Analogy
An office file contains employee details, salary and department. Someone attaches an URGENT sticker. The sticker doesn't change the file; it only provides extra information. Annotations work exactly like that.
#Built-in and Framework Annotations
| Built-in | Framework |
|---|---|
@Override | @Component, @Service, @Repository, @Controller |
@Deprecated | @Entity |
@SuppressWarnings | @Test |
@FunctionalInterface | @Autowired |
@SafeVarargs | @GetMapping |
#Custom Annotation
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@interface Author {
String name();
}Usage:
@Author(name = "Durga")
class Employee {
}Reflection can read this annotation.
#Retention Policy
One of the most important interview topics. Every annotation has a lifetime, and Java provides three retention policies:
| Policy | Where it exists | Visible to Reflection? | Example |
|---|---|---|---|
SOURCE | Only in source code — removed during compilation | ✗ | @Override |
CLASS | Stored in the .class file, but not loaded at runtime | ✗ | The default when no @Retention is given |
RUNTIME | Stored in the .class file and available at runtime | ✓ | @Service, @Entity, @Test |
@Retention(RetentionPolicy.RUNTIME) is mandatory for annotations that frameworks need to inspect.
#Target
@Target specifies where an annotation can be applied.
@Target(ElementType.TYPE)means only on types — classes, interfaces, enums, records and annotation types.
Other common targets: TYPE, FIELD, METHOD, CONSTRUCTOR, PARAMETER, LOCAL_VARIABLE, PACKAGE and ANNOTATION_TYPE.
@Target(ElementType.FIELD) // only fields, e.g. private String name;#Reading a Class Annotation
@Retention(RetentionPolicy.RUNTIME)
@interface Author {
String name();
}
@Author(name = "Durga")
class Employee {
}Class<Employee> cls = Employee.class;
Author author = cls.getAnnotation(Author.class);
System.out.println(author.name());DurgaReflection successfully reads annotation values.
isAnnotationPresent()
Instead of getting the annotation directly, we can check whether it exists:
if (cls.isAnnotationPresent(Author.class)) {
System.out.println("Present");
}PresentA very common interview question.
getAnnotation()
Returns one annotation (or null if absent):
Author author = cls.getAnnotation(Author.class);
author.name(); // "Durga"getAnnotations()
Returns all annotations. For a class marked @Author(name = "Durga") and @Deprecated:
Annotation[] annotations = cls.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}@com.demo.Author(name="Durga")
@java.lang.Deprecated(forRemoval=false, since="")(The exact text format of toString() differs slightly between Java versions.)
#Annotations on Methods, Fields and Constructors
class Employee {
@Deprecated
private String name;
@Deprecated
Employee() {
}
@Deprecated
public void work() {
}
}Method method = Employee.class.getDeclaredMethod("work");
System.out.println(method.isAnnotationPresent(Deprecated.class)); // true
Field field = Employee.class.getDeclaredField("name");
System.out.println(field.isAnnotationPresent(Deprecated.class)); // true
Constructor<?> constructor = Employee.class.getDeclaredConstructor();
System.out.println(constructor.isAnnotationPresent(Deprecated.class)); // true#Annotation Values
@Retention(RetentionPolicy.RUNTIME)
@interface EmployeeInfo {
int id();
String company();
}
@EmployeeInfo(id = 100, company = "OpenAI")
class Employee {
}EmployeeInfo info = Employee.class.getAnnotation(EmployeeInfo.class);
System.out.println(info.id());
System.out.println(info.company());100
OpenAI#Real-World Examples
Spring
@Service
class UserService {
}Spring performs operations conceptually similar to:
Class<?> cls = UserService.class;
if (cls.isAnnotationPresent(Service.class)) {
Object bean = cls.getDeclaredConstructor().newInstance();
}Spring creates the bean.
JUnit
@Test
public void loginTest() {
}JUnit performs operations similar to:
Method method = LoginTest.class.getDeclaredMethod("loginTest");
if (method.isAnnotationPresent(Test.class)) {
method.invoke(testObject);
}Hibernate
@Entity
class Employee {
}Class<?> cls = Employee.class;
if (cls.isAnnotationPresent(Entity.class)) {
// register entity
}#Multiple Annotations
@Service
@Deprecated
class Employee {
}
Annotation[] annotations = Employee.class.getAnnotations();Returns both @Service and @Deprecated.
#Meta-Annotations
Annotations can themselves have annotations:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Service {
}Here @Retention and @Target are meta-annotations. (@Documented, @Inherited and @Repeatable are the others.)
#Important Annotation Reflection Methods
| Element | Methods |
|---|---|
Class | isAnnotationPresent(), getAnnotation(), getAnnotations(), getDeclaredAnnotations() |
Method | getAnnotation(), isAnnotationPresent() |
Field | getAnnotation(), isAnnotationPresent() |
Constructor | getAnnotation(), isAnnotationPresent() |
#Interview Questions
Q1What is an annotation?
Metadata that provides additional information about Java program elements.
Q2Why are annotations called metadata?
Because they describe program elements without directly changing the program's execution logic.
Q3Which retention policy is required for Reflection?
RetentionPolicy.RUNTIME.
Q4Difference between SOURCE, CLASS and RUNTIME?
| Retention | In .class file | Visible to Reflection |
|---|---|---|
SOURCE | ✗ | ✗ |
CLASS (default) | ✓ | ✗ |
RUNTIME | ✓ | ✓ |
Q5Which method checks whether an annotation exists?
isAnnotationPresent().
Q6Which method returns one annotation?
getAnnotation().
Q7Which method returns all annotations?
getAnnotations().
Q8Can Reflection read annotation values?
Yes — e.g. annotation.name(), annotation.id().
Q9Why does Spring require runtime annotations?
Because Spring scans and processes annotations using Reflection after the application starts.
#Key Points to Remember
- Annotations are metadata.
- Reflection reads annotations only if they have
RetentionPolicy.RUNTIME. isAnnotationPresent()checks existence;getAnnotation()retrieves one;getAnnotations()retrieves all.- Reflection can inspect annotations on classes, methods, fields and constructors.
- Spring, Hibernate and JUnit are annotation-driven frameworks built on top of Reflection.
#Chapter Summary
Annotations let developers attach metadata to Java program elements. Reflection makes that metadata available at runtime, enabling frameworks to discover components, entities, test methods, REST endpoints and much more without hard-coding behaviour. This combination of annotations and Reflection is the foundation of modern Java enterprise development.