Java Reflection
1. Khái Niệm Cơ Bản
1.1 Reflection là gì?
- Định nghĩa:
- Khả năng kiểm tra và thay đổi behavior của class/object tại runtime
- Cho phép truy cập các thành phần private
-
Tạo instance mới mà không cần constructor trực tiếp
-
Ứng dụng:
- Framework Development
- IDE Development
- Unit Testing
- Dependency Injection
- Dynamic Proxy
1.2 Class Object và Class Loading
public class ReflectionBasics {
public void demonstrateClassLoading() {
// Lấy Class object
// Cách 1: Từ instance
String str = "Hello";
Class<?> class1 = str.getClass();
// Cách 2: Từ class literal
Class<?> class2 = String.class;
// Cách 3: Từ tên class
try {
Class<?> class3 = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// Thông tin về class
System.out.println("Class name: " + class1.getName());
System.out.println("Simple name: " + class1.getSimpleName());
System.out.println("Package: " + class1.getPackage());
System.out.println("Super class: " + class1.getSuperclass());
}
}
2. Truy Cập Class Members
2.1 Fields
- Đặc điểm:
- Truy cập fields (public, protected, private)
- Đọc và ghi giá trị
- Lấy thông tin metadata
public class FieldReflection {
public void demonstrateFields() {
Class<?> personClass = Person.class;
// Lấy tất cả fields
Field[] fields = personClass.getDeclaredFields();
// Lấy field cụ thể
try {
Field nameField = personClass.getDeclaredField("name");
// Cho phép truy cập private field
nameField.setAccessible(true);
// Tạo instance và set giá trị
Person person = new Person();
nameField.set(person, "John");
// Lấy giá trị
String name = (String) nameField.get(person);
// Thông tin về field
System.out.println("Type: " + nameField.getType());
System.out.println("Modifiers: " +
Modifier.toString(nameField.getModifiers()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.2 Methods
- Đặc điểm:
- Truy cập methods (public, protected, private)
- Invoke methods động
- Lấy thông tin parameters và return type
public class MethodReflection {
public void demonstrateMethods() {
Class<?> personClass = Person.class;
// Lấy tất cả methods
Method[] methods = personClass.getDeclaredMethods();
// Lấy method cụ thể
try {
Method setNameMethod = personClass.getDeclaredMethod(
"setName", String.class);
// Cho phép truy cập private method
setNameMethod.setAccessible(true);
// Tạo instance và gọi method
Person person = new Person();
setNameMethod.invoke(person, "John");
// Thông tin về method
System.out.println("Return type: " +
setNameMethod.getReturnType());
System.out.println("Parameter types: " +
Arrays.toString(setNameMethod.getParameterTypes()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.3 Constructors
- Đặc điểm:
- Tạo instance mới
- Truy cập constructors (public, protected, private)
- Lấy thông tin parameters
public class ConstructorReflection {
public void demonstrateConstructors() {
Class<?> personClass = Person.class;
// Lấy tất cả constructors
Constructor<?>[] constructors =
personClass.getDeclaredConstructors();
// Lấy constructor cụ thể
try {
Constructor<?> constructor =
personClass.getDeclaredConstructor(String.class, int.class);
// Cho phép truy cập private constructor
constructor.setAccessible(true);
// Tạo instance mới
Person person = (Person) constructor.newInstance("John", 30);
// Thông tin về constructor
System.out.println("Parameter types: " +
Arrays.toString(constructor.getParameterTypes()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. Annotations và Generic Type Information
3.1 Annotations
- Đặc điểm:
- Truy cập annotations tại runtime
- Lấy thông tin annotation values
- Kiểm tra presence của annotations
public class AnnotationReflection {
public void demonstrateAnnotations() {
Class<?> personClass = Person.class;
// Kiểm tra class annotations
if (personClass.isAnnotationPresent(Entity.class)) {
Entity entity = personClass.getAnnotation(Entity.class);
System.out.println("Table name: " + entity.name());
}
// Kiểm tra field annotations
for (Field field : personClass.getDeclaredFields()) {
if (field.isAnnotationPresent(Column.class)) {
Column column = field.getAnnotation(Column.class);
System.out.println("Column name: " + column.name());
}
}
// Kiểm tra method annotations
for (Method method : personClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(Transactional.class)) {
Transactional tx =
method.getAnnotation(Transactional.class);
System.out.println("Propagation: " +
tx.propagation());
}
}
}
}
3.2 Generic Type Information
- Đặc điểm:
- Truy cập generic type parameters
- Lấy thông tin về type arguments
- Xử lý type erasure
public class GenericReflection {
public void demonstrateGenerics() {
// Lấy generic type information
List<String> stringList = new ArrayList<>();
Class<?> listClass = stringList.getClass();
// Lấy type parameters của class
TypeVariable<?>[] typeParams = listClass.getTypeParameters();
// Lấy generic return type của method
try {
Method method = Repository.class
.getMethod("findById", Object.class);
Type returnType = method.getGenericReturnType();
if (returnType instanceof ParameterizedType) {
ParameterizedType paramType =
(ParameterizedType) returnType;
Type[] typeArgs = paramType.getActualTypeArguments();
for (Type typeArg : typeArgs) {
System.out.println("Type arg: " + typeArg);
}
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
4. Dynamic Proxy và Invocation Handler
4.1 Dynamic Proxy
- Đặc điểm:
- Tạo proxy objects tại runtime
- Implement interfaces động
- Intercept method calls
public class DynamicProxyExample {
// Interface cần proxy
interface UserService {
void save(User user);
User findById(Long id);
}
// Invocation handler
class LoggingHandler implements InvocationHandler {
private final Object target;
public LoggingHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
System.out.println("Before method: " +
method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " +
method.getName());
return result;
}
}
public void demonstrateProxy() {
// Tạo service implementation
UserService userService = new UserServiceImpl();
// Tạo proxy
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class<?>[] { UserService.class },
new LoggingHandler(userService)
);
// Sử dụng proxy
User user = new User("John");
proxy.save(user);
User found = proxy.findById(1L);
}
}
4.2 Method Interception
- Đặc điểm:
- Thêm behavior trước/sau method calls
- Thay đổi parameters và return values
- Exception handling
public class MethodInterceptionExample {
// Custom annotation for transaction
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Transactional {}
// Transaction invocation handler
class TransactionHandler implements InvocationHandler {
private final Object target;
public TransactionHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// Kiểm tra annotation
if (method.isAnnotationPresent(Transactional.class)) {
return executeInTransaction(method, args);
}
return method.invoke(target, args);
}
private Object executeInTransaction(Method method,
Object[] args) throws Throwable {
try {
beginTransaction();
Object result = method.invoke(target, args);
commitTransaction();
return result;
} catch (Exception e) {
rollbackTransaction();
throw e;
}
}
private void beginTransaction() {
System.out.println("Beginning transaction");
}
private void commitTransaction() {
System.out.println("Committing transaction");
}
private void rollbackTransaction() {
System.out.println("Rolling back transaction");
}
}
}
5. Best Practices và Performance Considerations
5.1 Best Practices
public class ReflectionBestPractices {
// 1. Cache reflection objects
public class CachedReflection {
private static final Map<String, Method> methodCache =
new ConcurrentHashMap<>();
public Object invokeMethod(Object target,
String methodName, Object... args)
throws Exception {
Method method = methodCache.computeIfAbsent(
methodName,
name -> {
try {
return target.getClass()
.getDeclaredMethod(name);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
);
return method.invoke(target, args);
}
}
// 2. Sử dụng setAccessible một lần
public class AccessibleExample {
private final Field field;
public AccessibleExample(Class<?> clazz, String fieldName)
throws NoSuchFieldException {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
}
public Object getValue(Object target)
throws IllegalAccessException {
return field.get(target);
}
}
// 3. Exception handling
public class SafeReflection {
public Object safeInvoke(Method method, Object target,
Object... args) {
try {
return method.invoke(target, args);
} catch (IllegalAccessException e) {
throw new RuntimeException(
"Access denied to method: " + method.getName(), e);
} catch (InvocationTargetException e) {
throw new RuntimeException(
"Method threw exception: " + method.getName(),
e.getTargetException());
}
}
}
}
5.2 Performance Considerations
```java
public class ReflectionPerformance {
// 1. Avoid reflection in loops
public void performanceExample() {
List
// BAD: Reflection in loop
for (String s : list) {
try {
Method method = String.class
.getMethod("length");
method.invoke(s);
} catch (Exception e) {
e.printStackTrace();
}
}
// GOOD: Cache reflection objects
try {
Method method = String.class.getMethod("length");
for (String s : list) {
method.invoke(s);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 2. Use direct access when possible
public class DirectVsReflection {
public void directAccess(Person person) {
// Direct access - faster
String name = person.getName();
}
public void reflectiveAccess(Person person) {
// Reflective access - slower
try {
Method method = Person.class
.getMethod("getName");
String name = (String) method.invoke(person);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 3. Batch operations
public class BatchProcessing {
public void processBatch(List<Object> objects,
String methodName) {
try {
// Get method once
Method method = objects.get(0).getClass()
.getMethod(methodName);
method.setAccessible(true);
// Process batch
for (Object obj : objects) {
method.invoke(obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}