Annotations & Reflection — Metadata That Powers Modern Java
Annotation ও Reflection — আধুনিক Java framework-এর ভিত্তি
1. What Are Annotations?
An annotation is structured metadata that you attach to a class, method, field, parameter, or
even another annotation. Annotations do not change what your code does on their own — they add
information that tools, compilers, or runtime frameworks can read and act upon. Every @Override
you have written, every @Test, every @Autowired in Spring — all annotations.
@Override, @Test, বা Spring-এ @Autowired লিখেছেন — সবই annotation।
Reflection is the partner technology: Java code that inspects Java code at runtime. Together, annotations + reflection let frameworks like Spring, Hibernate, and Jackson read your metadata and wire up dependency injection, ORM mapping, or JSON serialization without you writing glue code.
2. Built-in Annotations You Already Know
The Java platform ships with a handful of core annotations. These are the ones every Java developer sees on day one.
| Annotation | Meaning | বাংলায় |
|---|---|---|
@Override | Asserts you are overriding a superclass method. | আপনি parent class-এর method override করছেন — ভুল হলে compiler জানিয়ে দেবে। |
@Deprecated | Marks an API as obsolete; generates warning on use. | এই API আর ব্যবহার করবেন না — warning দেখাবে। |
@SuppressWarnings | Tells compiler to silence specific warnings. | নির্দিষ্ট warning চেপে রাখতে বলে। |
@FunctionalInterface | Guarantees an interface has exactly one abstract method. | Interface-এ ঠিক একটিই abstract method থাকবে — lambda-compatible। |
@SafeVarargs | Suppresses unchecked-cast warning on generic varargs. | Generic varargs-এ unchecked warning বন্ধ করে। |
class Animal {
public String speak() { return "some sound"; }
}
class Dog extends Animal {
// @Override catches typos at compile time
@Override
public String speak() { return "Bhau!"; }
}
class Main {
public static void main(String[] args) {
Animal a = new Dog();
System.out.println(a.speak());
}
}
3. Meta-Annotations — Annotations on Annotations
When you write your own annotation you must tell Java two things — where it can be
applied (@Target) and how long the metadata should live
(@Retention). These annotations-on-annotations are called meta-annotations.
@Target) এবং কতদিন metadata টিকে থাকবে (@Retention)। এগুলোকে meta-annotation বলা হয়।
4. Writing a Custom Annotation & Reading It with Reflection
Let us build a mini "@Audit" annotation that a bKash-scale fintech might use to tag sensitive methods. We define the annotation, attach it to a method, then walk the class with reflection to discover every audited method at runtime.
@Audit annotation তৈরি করবেন। তারপর reflection দিয়ে runtime-এ class-এর সব audited method খুঁজে বার করবেন।
import java.lang.annotation.*;
import java.lang.reflect.Method;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Audit {
String value() default "general";
}
class PaymentService {
@Audit("money-transfer")
public void sendMoney(String to, int taka) {
System.out.println("Sent " + taka + " BDT to " + to);
}
public void ping() { System.out.println("pong"); }
}
class Main {
public static void main(String[] args) throws Exception {
for (Method m : PaymentService.class.getDeclaredMethods()) {
Audit a = m.getAnnotation(Audit.class);
if (a != null) {
System.out.println("[AUDITED: " + a.value() + "] " + m.getName());
}
}
}
}
@Audit metadata in the .class file (because of
RetentionPolicy.RUNTIME). At runtime we used Class.getDeclaredMethods() to
walk every method and getAnnotation() to read the tag. This is a toy version of
exactly how Spring discovers @Transactional, or how JUnit finds @Test.
5. The Reflection API — A Quick Tour
Everything reflective hangs off a Class<?> object. From there you can list fields,
invoke methods by name, read generic type parameters, and construct objects without calling
new directly.
Class<?> object থেকে। সেখান থেকে field list করা, নাম দিয়ে method call করা, type parameter পড়া এবং new ছাড়াই object তৈরি — সব সম্ভব।
import java.lang.reflect.*;
class Account {
private String owner = "Fatima";
public int balance() { return 1000; }
}
class Main {
public static void main(String[] args) throws Exception {
Class<?> c = Account.class;
System.out.println("Class: " + c.getName());
for (Field f : c.getDeclaredFields()) {
System.out.println(" field -> " + f.getType().getSimpleName() + " " + f.getName());
}
for (Method m : c.getDeclaredMethods()) {
System.out.println(" method -> " + m.getName());
}
// Construct an Account without calling `new` directly
Account a = (Account) c.getDeclaredConstructor().newInstance();
Method bal = c.getDeclaredMethod("balance");
System.out.println("balance = " + bal.invoke(a));
}
}
6. When Not to Reflect
✅ Good Use of Reflection
- Writing frameworks (Spring, JUnit, Jackson)
- Plugin systems — load classes by name
- Serialization libraries
- Build-time tools that scan annotations
⚠️ Bad Use of Reflection
- Accessing private fields "just because"
- Replacing a simple method call — 10–50× slower
- Any hot-path code
- Anything the type system could express directly
Reflection compile-time safety নষ্ট করে এবং ধীর। তাই আধুনিক production code-এ annotation processor ও code generation (Lombok, MapStruct, Dagger) বেশি চলে — build time-এ annotation পড়ে সাধারণ Java কোড তৈরি হয়, runtime-এ reflection লাগে না।
7. Vocabulary (শব্দভাণ্ডার)
| Term | Meaning | বাংলায় |
|---|---|---|
| Annotation | Structured metadata attached to code. | কোডের উপর বসানো metadata। |
| Meta-annotation | An annotation applied to another annotation. | অন্য annotation-এর উপর বসানো annotation। |
| @Retention | How long metadata survives: SOURCE/CLASS/RUNTIME. | Metadata কতক্ষণ থাকবে। |
| @Target | Where an annotation may be applied. | কোন element-এ বসানো যাবে। |
| Reflection | Reading / invoking code structure at runtime. | Runtime-এ কোডের গঠন পড়া ও চালানো। |
| Annotation processor | Build-time plugin that reads annotations and generates code. | Build-time plugin — annotation পড়ে নতুন কোড তৈরি করে। |
8. Practice Problems
Try each problem yourself before opening the answer.
-
Create a custom annotation
@Versionthat takes an integer value and attach it to a class.একটি custom annotation@Versionতৈরি করুন যা integer value নেবে, এবং একটি class-এ প্রয়োগ করুন। Reflection দিয়ে সেটি প্রিন্ট করুন।✨ Show Answer (উত্তর দেখুন)
Main.javaimport java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface Version { int value(); } @Version(3) class App {} class Main { public static void main(String[] args) { Version v = App.class.getAnnotation(Version.class); System.out.println("App version = " + v.value()); } } -
Explain the difference between
RetentionPolicy.CLASSandRetentionPolicy.RUNTIME.RetentionPolicy.CLASSএবংRetentionPolicy.RUNTIME-এর পার্থক্য ব্যাখ্যা করুন।✨ Show Answer
Answer: Both retain the annotation in the compiled
.classfile, but onlyRUNTIMEmakes it visible to the reflection API after the class is loaded.CLASSis for tools that scan bytecode offline (e.g. some static analyzers). If a framework such as Spring needs to discover your annotation while the JVM is running, you MUST useRUNTIME.দুটিই
.classফাইলে annotation রাখে, তবে শুধুRUNTIMEclass load হওয়ার পর reflection API-তে দৃশ্যমান।CLASSbytecode scanner-দের জন্য। Spring-এর মতো framework যদি runtime-এ annotation খুঁজে পেতে চায়,RUNTIME-ই দিতে হবে। -
Use reflection to list every method of
java.util.ArrayListthat starts with"add".Reflection ব্যবহার করেjava.util.ArrayList-এর সেই method-গুলো list করুন যেগুলো"add"দিয়ে শুরু হয়।✨ Show Answer
Main.javaimport java.util.ArrayList; import java.lang.reflect.Method; class Main { public static void main(String[] args) { for (Method m : ArrayList.class.getDeclaredMethods()) { if (m.getName().startsWith("add")) { System.out.println(m.getName() + " / params=" + m.getParameterCount()); } } } } -
Why is
Method.invoke()slower than a direct call? Give one way to mitigate it.Method.invoke()কেন সাধারণ method call-এর চেয়ে ধীর? এটি কমানোর একটি উপায় বলুন।✨ Show Answer
Answer: Reflective invocation goes through access checks, autoboxing of primitive arguments into
Object[], and dynamic dispatch — none of which the JIT can optimize as well as a direct virtual call. Mitigations: (1) cache theMethodobject once, do not look it up per call, (2) callsetAccessible(true)once to skip access checks, (3) for hot paths, useMethodHandleviajava.lang.invoke— its JIT-visible and nearly as fast as a direct call. -
Build a tiny DI container: given a class that has a
@Inject-annotated field, create an instance and set the field to a hard-coded"PaymentAPI"string.একটি ছোট DI container বানান: class-এ@Inject-annotated field থাকলে সেটিতে একটি hard-coded"PaymentAPI"string inject করুন।✨ Show Answer
Main.javaimport java.lang.annotation.*; import java.lang.reflect.Field; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @interface Inject {} class OrderService { @Inject String paymentApi; public String toString() { return "OrderService using " + paymentApi; } } class Main { public static void main(String[] args) throws Exception { OrderService o = OrderService.class.getDeclaredConstructor().newInstance(); for (Field f : OrderService.class.getDeclaredFields()) { if (f.isAnnotationPresent(Inject.class)) { f.setAccessible(true); f.set(o, "PaymentAPI"); } } System.out.println(o); } }এটিই Spring-এর
@Autowired-এর সরলতম সংস্করণ।
Summary — Module 42
Annotations are structured metadata on your code; reflection is the runtime ability to read that metadata. Together they are the invisible machinery under Spring, JUnit, Hibernate, and Jackson. Use them to build frameworks — but reach for annotation processors and plain code for performance-critical paths.