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 list = Arrays.asList("a", "b", "c");

    // 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();
        }
    }
}

}