Java Reflection

  1. 1. Java Reflection
    1. 1.1. 1. Khái niệm:
    2. 1.2. 2. Các hàm của Java reflection:
      1. 1.2.1. Lấy ra thuộc tính của một lớp (Field):
      2. 1.2.2. Lấy ra phương thức của một lớp:
        1. 1.2.2.1. Phương thức getMethod() và getMethods():
        2. 1.2.2.2. Phương thức getDeclaredMethod() và getDeclaredMethods():
      3. 1.2.3. Phương thức getConstructor() và getConstructors():
      4. 1.2.4. Lấy ra một lớp mà không thể thao tác trực tiếp:
        1. 1.2.4.1. Phương thức forName

Lớp Java Reflection là một lớp thường xuyên được sử dụng vì sự tiện nghi của nó, nên chúng ta cũng có thể thường xuyên thấy những lớp này khi đi sâu vào những lỗ hổng của Java. Ngoài ra, trong Java Deserialization ta thường dùng lớp này để có thể truy cập vào các thuộc tính Private.

Trước khi đọc bài viết này, các bạn nên tìm hiểu lớp Class, Object.

Tản mạn thế là đủ rồi, ta cùng đi vào bài viết nào o( ̄▽ ̄)ブ

Java Reflection

1. Khái niệm:

Reflection là một tính năng của java, nó cho phép một chương trình đang chạy trực tiếp lấy ra các thuộc tính, phương thức,… và thao tác với nó.

Một điểm đặc biệt của Java reflection là nó cho phép thay đổi các thuộc tính Private của một Object.

2. Các hàm của Java reflection:

Ở đây mình chỉ liệt kê ra những hàm thường dùng của Reflection.

Lấy ra thuộc tính của một lớp (Field):

Trong lớp Class có hai phương thức để lấy ra trường ở trong một lớp là getDeclaredFields() và getDeclaredField(String name).

  • Phương thức getDeclaredFields() sẽ lấy ra tất cả những trường có ở trong class của object đó.
  • Phương thức getDeclaredField(String name) sẽ lấy ra một trường có tên là String mà chúng ta truyền vào.

Hai phương thức này tương tự nhau chỉ là phương thức getDeclaredFields() sẽ lấy ra tất cả các field của Class đó rồi cho vào một mảng nên các bạn tự tìm hiểu nhé..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.lang.reflect.Field;

class person{
private String name;
public person(String name){
this.name = name;
}
public void setName(String name){
this.name = name;
}
@Override
public String toString(){
return "Person: "+ "name = '"+ name + "'";
}
}

public class reflectionClass {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
person a = new person("helen");
Field name = a.getClass().getDeclaredField("name");
name.setAccessible(true);//Cho phép truy cập tạm thời vào biến name để thay đổi, đây là cái hay của reflection.
name.set(a, "Sayo");
System.out.println(a);
}
}

Lấy ra phương thức của một lớp:

Method Public Non-public Inherited
getMethod & getMethods ✔️ ✔️
getDeclaredMethod & getDeclaredMethods ✔️ ✔️
  • Phương thức getMethod() và getMethods():

    Hàm getMethod() và getMethods() sẽ có thể lấy những method public và những method được kế thừa từ lớp cha của nó. Lớp getMethod() sẽ trả về một phương thức xác định, còn lớp getMethods() sẽ trả về một mảng các phương thức.
  • Phương thức getDeclaredMethod() và getDeclaredMethods():

    Hàm getDeclaredMethod() và getDeclaredMethods() sẽ lấy ra những phương thức được định nghĩa ở trong class hiện tại, bao gồm cả Public và Non-public nhưng không thể lấy những class kế thừa như của getMethod(). Tương tự như vậy, lớp getDeclaredMethod() sẽ trả về một phương thức xác định, còn lớp getDeclaredMethods() sẽ trả về một mảng các phương thức.

Các phương thức đều tương tự nhau nên mình sẽ chỉ đưa ra một ví dụ:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class person{
private String name;
public person(String name){
this.name = name;
}
public void setName(String name){
this.name = name;
}
public void work(String a, String b){
System.out.println("Do something....");
}
@Override
public String toString(){
return "Person: "+ "name = '"+ name + "'";
}
}

public class reflectionClass {
public static void main(String[] args) throws Exception {
person a = new person("helen");
Method method = a.getClass().getMethod("work", String.class, String.class);
method.invoke(a, "a", "b");
}
}
  • Class.getMethod() nhận tham số đầu là string với tên của method, các tham số sau là lớp của đối số mà method cần lấy ra nhận vào. Như ví dụ trên, lớp work nhận vào 2 tham số là String nên chúng ta sẽ truyền vào String.class,…

  • method lấy ra bây giờ là một object của lớp Method bây giờ nó đại diện cho các method của những object thuộc chung một class có method được lấy ra. Trong lớp Method có một hàm invoke() dùng để gọi cái method đó. Và đương nhiên method bây giờ chưa phải là của object nào mà nó chỉ chung chung, bây giờ hàm invoke() sẽ nhận đối số đầu tiên là object chứa method đó, các giá trị sau là các tham số của method.

  • Lưu ý method chỉ sử dụng nếu các object là chung một class.

Phương thức getConstructor() và getConstructors():

Phương thức này giúp chúng ta lấy ra một Constructor của một lớp và đưa vào lớp Constructor, từ Constructor được lấy ra, chúng ta có thể tạo một Object bằng hàm newInstance() ở trong lớp Constructor.
Phương thức getConstructors() tương tự, chỉ khác là nó sẽ đưa vào mảng.

1
2
3
4
5
6
public static void main(String[] args) throws Exception {
Class<person> personClass = person.class;
Constructor<person> personConstructor = personClass.getConstructor(String.class);
person a = personConstructor.newInstance("Makune");
System.out.println("person: " + a);
}

Lấy ra một lớp mà không thể thao tác trực tiếp:

Phương thức forName

Để lấy ra một class chúng ta dùng phương thức forName.

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) throws Exception {
//Lấy ra lớp AnnotationInvocationHandler
Class aihClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
//Lấy ra Constructor của lớp AnnotationInvocationHandler
Constructor aihConstructor = aihClass.getDeclaredConstructor(Class.class, Map.class);
//Lớp này khởi tạo default nên chúng ta phải setAccessible(true)
aihConstructor.setAccessible(true);
//Sử dụng constructor
//Vì chúng ta không có quyền với lớp AnnotationInvocationHandler nên chúng ta không thể tạo Object kiểu AnnotationInvocationHandler, do đó chúng ta phải đặt kiểu là Object hoặc kiểu mà lớp AnnotationInvocationHandler kế thừa.
Object aihObject = aihConstructor.newInstance(Override.class, new HashMap<>());
}