[Java Backend Zero to Hello] BÀI 2.9: ANNOTATION & REFLECTION
📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 2: Java Core Nâng Cao | Bài 24/86
BÀI 2.9: ANNOTATION & REFLECTION
Mục tiêu
- Hiểu Annotation và cách sử dụng
- Tạo Custom Annotation
- Sử dụng Reflection API
- Áp dụng vào Framework
1. ANNOTATION LÀ GÌ?
Annotation là metadata (siêu dữ liệu) gắn vào code, cung cấp thông tin cho compiler, tool hoặc runtime.
Các Annotation có sẵn
@Override // Ghi đè method
@Deprecated // Không nên dùng
@SuppressWarnings // Tắt cảnh báo
@FunctionalInterface // Interface có 1 abstract method
@SafeVarargs // Varargs an toàn
2. SỬ DỤNG ANNOTATION
2.1 Áp dụng cho các phần tử
@Deprecated
public void oldMethod() { }
@Override
public String toString() { return ""; }
@SuppressWarnings("unchecked")
public List rawList() { return new ArrayList(); }
2.2 Annotation với tham số
@SuppressWarnings(value = "unchecked")
@SuppressWarnings("unchecked") // value có thể bỏ
@Deprecated(since = "2.0", forRemoval = true)
public void oldMethod() { }
3. TẠO CUSTOM ANNOTATION
3.1 Khai báo
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
String value() default "";
int priority() default 0;
}
3.2 Meta-Annotation
| Annotation | Mô tả |
|---|---|
@Retention |
Khi nào annotation tồn tại |
@Target |
Áp dụng cho đâu |
@Documented |
Có trong Javadoc |
@Inherited |
Kế thừa cho class con |
@Repeatable |
Lặp lại nhiều lần |
3.3 RetentionPolicy
@Retention(RetentionPolicy.SOURCE) // Chỉ source code, mất khi compile
@Retention(RetentionPolicy.CLASS) // Trong .class, mất khi runtime
@Retention(RetentionPolicy.RUNTIME) // Có ở runtime - dùng với reflection
3.4 ElementType
@Target(ElementType.TYPE) // Class, interface, enum
@Target(ElementType.FIELD) // Field
@Target(ElementType.METHOD) // Method
@Target(ElementType.PARAMETER) // Parameter
@Target(ElementType.CONSTRUCTOR) // Constructor
@Target(ElementType.LOCAL_VARIABLE) // Biến local
@Target(ElementType.ANNOTATION_TYPE) // Annotation
// Nhiều target
@Target({ElementType.METHOD, ElementType.FIELD})
3.5 Ví dụ đầy đủ
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Test {
String name() default "";
int timeout() default 1000;
String[] tags() default {};
}
3.6 Sử dụng
public class UserService {
@Test(name = "create user", timeout = 5000, tags = {"unit", "fast"})
public void createUser() {
// ...
}
@Test
public void deleteUser() {
// ...
}
}
4. REFLECTION
Reflection cho phép kiểm tra và thao tác với class, method, field tại runtime.
4.1 Lấy Class object
// Cách 1: Class.forName()
Class<?> clazz = Class.forName("com.example.User");
// Cách 2: .class
Class<User> userClass = User.class;
// Cách 3: getClass()
User user = new User();
Class<?> clazz = user.getClass();
4.2 Thông tin Class
Class<?> clazz = User.class;
String name = clazz.getName(); // com.example.User
String simpleName = clazz.getSimpleName(); // User
Package pkg = clazz.getPackage();
Class<?> superClass = clazz.getSuperclass();
Class<?>[] interfaces = clazz.getInterfaces();
int modifiers = clazz.getModifiers();
boolean isPublic = Modifier.isPublic(modifiers);
boolean isAbstract = Modifier.isAbstract(modifiers);
4.3 Constructor
Class<?> clazz = User.class;
// Lấy constructor
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Constructor<?>[] constructors = clazz.getConstructors();
Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();
// Tạo instance
Object user = constructor.newInstance("An", 25);
4.4 Field
Class<?> clazz = User.class;
// Lấy field
Field nameField = clazz.getDeclaredField("name");
Field[] fields = clazz.getDeclaredFields();
// Truy cập private field
nameField.setAccessible(true);
nameField.set(user, "Bình");
String name = (String) nameField.get(user);
4.5 Method
Class<?> clazz = User.class;
// Lấy method
Method method = clazz.getDeclaredMethod("setName", String.class);
Method[] methods = clazz.getDeclaredMethods();
// Gọi method
method.setAccessible(true);
method.invoke(user, "Châu");
// Gọi static method
Method staticMethod = clazz.getDeclaredMethod("createDefault");
staticMethod.setAccessible(true);
Object result = staticMethod.invoke(null);
5. ANNOTATION + REFLECTION
// Định nghĩa annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
String name() default "";
}
// Class sử dụng
public class UserService {
@Test(name = "test create")
public void testCreate() { }
@Test
public void testDelete() { }
}
// Framework đọc annotation
public class TestRunner {
public static void runTests(Class<?> clazz) throws Exception {
Object instance = clazz.getDeclaredConstructor().newInstance();
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(Test.class)) {
Test annotation = method.getAnnotation(Test.class);
System.out.println("Running: " + annotation.name());
method.invoke(instance);
}
}
}
}
6. ỨNG DỤNG THỰC TẾ
6.1 Dependency Injection (như Spring)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Inject {}
public class UserService {
@Inject
private UserRepository userRepository;
}
// Framework tự động inject
public class Container {
public static <T> T create(Class<T> clazz) throws Exception {
T instance = clazz.getDeclaredConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
field.setAccessible(true);
field.set(instance, create(field.getType()));
}
}
return instance;
}
}
6.2 ORM (như JPA/Hibernate)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
String name() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Entity {
String table() default "";
}
@Entity(table = "users")
public class User {
@Column(name = "user_name")
private String name;
}
7. HẠN CHẾ CỦA REFLECTION
- Chậm hơn gọi trực tiếp
- Phá vỡ encapsulation (truy cập private)
- Khó debug
- Không type-safe tại compile time
💡 Chỉ dùng Reflection khi thật sự cần (Framework, Library).
8. BÀI TẬP THỰC HÀNH
Bài 1: Annotation Validator
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NotNull {}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Min {
int value();
}
public class Validator {
public static List<String> validate(Object obj) throws IllegalAccessException {
List<String> errors = new ArrayList<>();
for (Field field : obj.getClass().getDeclaredFields()) {
field.setAccessible(true);
Object value = field.get(obj);
if (field.isAnnotationPresent(NotNull.class) && value == null) {
errors.add(field.getName() + " không được null");
}
if (field.isAnnotationPresent(Min.class) && value instanceof Number) {
int min = field.getAnnotation(Min.class).value();
if (((Number) value).intValue() < min) {
errors.add(field.getName() + " phải >= " + min);
}
}
}
return errors;
}
}
Bài 2: Auto Mapper
public static <T> T map(Object source, Class<T> targetClass) throws Exception {
T target = targetClass.getDeclaredConstructor().newInstance();
for (Field sourceField : source.getClass().getDeclaredFields()) {
try {
Field targetField = targetClass.getDeclaredField(sourceField.getName());
sourceField.setAccessible(true);
targetField.setAccessible(true);
targetField.set(target, sourceField.get(source));
} catch (NoSuchFieldException ignored) {
}
}
return target;
}
Bài 3: JSON-like toString
public static String toJson(Object obj) throws IllegalAccessException {
StringBuilder sb = new StringBuilder("{");
Field[] fields = obj.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
fields[i].setAccessible(true);
if (i > 0) sb.append(",");
sb.append("\"").append(fields[i].getName()).append("\":");
Object value = fields[i].get(obj);
sb.append(value instanceof String ? "\"" + value + "\"" : value);
}
sb.append("}");
return sb.toString();
}
9. TÓM TẮT
| Khái niệm | Mô tả |
|---|---|
| Annotation | Metadata cho code |
@Retention |
Thời điểm tồn tại |
@Target |
Phạm vi áp dụng |
| Reflection | Truy cập class tại runtime |
Class.forName |
Lấy Class object |
getDeclaredField |
Lấy field (kể cả private) |
setAccessible(true) |
Bỏ qua access modifier |
invoke |
Gọi method |
Bài tiếp theo: 2.10 Enum & Nested class
🧭 Điều Hướng Series
⬅️ Bài trước: BÀI 2.8: I/O & FILE
📋 Lộ trình tổng quan: Xem Toàn Bộ Series
➡️ Bài tiếp theo: BÀI 2.10: ENUM & NESTED CLASS
All rights reserved