Gọi private method trong Java với reflection

Trong Java, các private method thường được sử dụng để ngăn chặn bên ngoài class gọi đến những method này. Trong một số trường hợp đặc biệt chúng ta vẫn có thể gọi các private method từ bên ngoài class thông qua Java reflection.

 Ngoài ra trong các ứng dụng Spring chúng ta được cung cấp một ReflectionTestUtils class có thể làm điều tương tự.

Chuẩn bị

Giả sử chúng ta có một LongArrayUtil có các method như sau:

public class LongArrayUtil {
    public static int indexOf(long[] array, long target) {
        return indexOf(array, target, 0, array.length);
    }

    private static int indexOf(long[] array, long target, int start, int end) {
        for (int i = start; i < end; i++) {
            if (array[i] == target) {
                return i;
            }
        }
        return -1;
    }
}

Chúng ta có thể thấy indexOf() đều được đặt ở mức private nghĩa là thông thường chúng chỉ được sử dụng nội bộ bên trong class.

Java reflection API

Trình biên dịch sẽ ngăn chặn chúng ta gọi đến các private method được định nghĩa bên trong một class bất kỳ thế nhưng chúng ta vẫn có thể gọi chúng từ bên ngoài thông qua Java reflection. 

public class Main {

    public static void main(String[] args) throws Exception {
        Method indexOfMethod = LongArrayUtil.class.getDeclaredMethod(
                "indexOf", long[].class, long.class, int.class, int.class);
        indexOfMethod.setAccessible(true);
        long arr[] = {1L, 2L, 3L};
        int value = (int) indexOfMethod.invoke(
                LongArrayUtil.class, arr, 2L, 0, arr.length);
        System.out.println("Index of: " + value);
    }
}

Trong đó:

  • getDeclaredMethod() – dùng để kiểm tra và trả về một Method instance nếu method tồn tại trong class
  • setAccessible(true) – cho phép chúng ta thay đổi khả năng truy cập của method từ không được thành được phép.
  • invoke() – dùng hàm này để gọi private

ReflectionTestUtils

Nếu các bạn đang phát triển các ứng dụng dựa trên nền tảng Spring framework thì họ đang cung cấp sẵn ReflectionTestUtils class cho phép làm điều tương tự như trên. Hãy đảm bảo rằng project của bạn có import spring test dependency

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.4</version>
    <scope>test</scope>
</dependency>

Và bây giờ chúng ta có thể sử dụng ReflectionTestUtils#invokeMethod() method để gọi một private method mà không cần phải trải qua các bước trung gian như khi sử dụng Java reflection thuần. 

public class Main {

    public static void main(String[] args) throws Exception {
        long arr[] = {1L, 2L, 3L};
        int value = ReflectionTestUtils.invokeMethod(
                     LongArrayUtil.class, "indexOf", someLongArray, 1L, 1, someLongArray.length);
        System.out.println("Index of: " + value);
    }
}

Lưu ý khi sử dụng Java reflection

Việc sử dụng Java reflection để gọi đến các private method sẽ phá vỡ tính đóng gói trong lập trình hướng đối tượng và có thể gây ra các rủi ro nghiêm trọng hoặc thậm chí là không thể thực hiện được. Khi quyết định sử dụng chúng ta nên cân nhắc các điều sau:

  • Liệu Java Security Manager có cho phép chúng ta làm điều này tại thời điểm runtime hay không?
  • Liệu các method mà chúng ta gọi trong reflection có tồn tại trong mã nguồn hiện tại và tương lai hay không?
  • Liệu chúng ta có thể refactor code chuyển đổi private sang public để phục vụ các nhu cầu bên ngoài class hay không?

Tóm lược

Qua bài biết này chúng ta đã biết cách sử dụng Java reflection để gọi một private method trong Java. Tuy nhiên lý thuyết là vậy nhưng các bạn vẫn nên cân nhắc sử dụng các biện pháp thay vì sử dụng reflection có thể gây ra các lỗi nghiêm trọng về sau, mã nguồn cũng trở nên khó bảo trì hơn khi sử dụng reflection quá nhiều.

Nguồn: Baeldung

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x