Nội dung
Java Reflection là gì?
Java là một ngôn ngữ hướng đối tượng (Object-oriented), thông thường bạn cần tạo ra một đối tượng và bạn có thể truy cập vào các trường (field), hoặc gọi phương thức (method) của đối tượng này thông qua toán tử dấu chấm ( . ).
Java Reflection giới thiệu một cách tiếp cận khác, bạn có thể truy cập vào một trường của một đối tượng nếu bạn biết tên của trường đó. Hoặc bạn có thể gọi một phương thức của đối tượng nếu bạn biết tên phương thức, các kiểu tham số của phương thức, và các giá trị tham số để truyền vào …
Java Reflecion cho phép bạn truy cập, sửa đổi cấu trúc và hành vi của một đối tượng tại thời gian chạy (runtime) của chương trình. Đồng thời nó cho phép bạn truy cập vào các thành viên private (private member) tại mọi nơi trong ứng dụng, điều này không được phép với cách tiếp cận truyền thống.
Java Reflection khá mạnh mẽ và rất hữu ích đối với những ai hiểu rõ về nó. Ví dụ, bạn có thể ánh xạ (mapping) đối tượng (object) thành table dưới database tại thời điểm runtime. Kỹ thuật này các bạn có thể thấy rõ nhất ở JPA và Hibernate.
Một số class được sử dụng trong bài viết này
Animal.java
package com.gpcoder.reflection; public abstract class Animal { public String getLocation() { return "VietNam"; } public abstract int getNumberOfLegs(); }
Say.java
package com.gpcoder.reflection; public interface Say { public String say(); }
Cat.java
package com.gpcoder.reflection; @Excel(name = "Cat") public class Cat extends Animal implements Say { // Public fields public static final String SAY = "Meo meo"; public static final int NUMBER_OF_LEGS = 4; // Private fields @ExcelColumn(index = 0, title = "Name") private String name; @ExcelColumn(index = 1, title = "Age") public int age; // Construnctors public Cat() { } public Cat(String name) { this.name = name; this.age = 1; } public Cat(String name, int age) { this.name = name; this.age = age; } /** * Implements from interface Say. */ @Override public String say() { return SAY; } /** * Implements from Animal. */ @Override public int getNumberOfLegs() { return NUMBER_OF_LEGS; } // Private Method. private void setName(String name) { this.name = name; } // Getters and setters public String getName() { return this.name; } public int getAge() { return this.age; } public void setAge(int age) { this.age = age; } }
Excel.java
package com.gpcoder.reflection; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Target(ElementType.TYPE) @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface Excel { int index() default 0; String name() default "Sheet 1"; }
ExcelColumn.java
package com.gpcoder.reflection; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Target(ElementType.FIELD) @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface ExcelColumn { int index(); String title(); String description() default "Default value"; }
Kiến trúc của Java Reflection API
Các lớp được dùng trong reflection nằm trong hai package là java.lang và java.lang.reflect. Package java.lang.reflect bao gồm ba lớp chính mà bạn cần biết là Constructor, Field và Method:
- Class<T>: lớp này đại diện cho các lớp, interface và chứa các phương thức dùng để lấy các đối tượng kiểu Constructor, Field, Method,…
- AccessibleObject: các kiểm tra về phạm vi truy xuất (public, private, protected) của field, method, constructor sẽ được bỏ qua. Nhờ đó bạn có thể dùng reflection để thay đổi, thực thi các thành phần này mà không cần quan tâm đến phạm vi truy xuất của nó.
- Constructor: chứa các thông tin về một constructor của lớp.
- Field: chứa các thông tin về một field của lớp, interface.
- Method: chứa các thông tin về một phương thức của lớp, interface.
Lớp (Classes)
Khi sử dụng Java Reflection để duyệt qua một class thì việc đầu tiên thường phải làm đó là có được một đối tượng kiểu Class, từ các đối tượng kiểu Class chúng ta có thể lấy được các thông tin về:
- Class Name
- Class Modifies (public, private, synchronized etc.)
- Package Info
- Superclass
- Implemented Interfaces
- Constructors
- Methods
- Fields
- Annotations
Tạo đối tượng Class<>
Đối tượng kiểu Class được tạo ra bằng cách sử dụng phương thức static Class.forName(). Cách này thường được sử dụng khi chỉ biên được tên lớp lúc thực thi (runtime):
try { Class c = Class.forName("com.gpcoder.Cat"); // ... } catch (ClassNotFoundException e) { System.err.println(e); }
Trong trường hợp không tìm thấy lớp tương ứng, phương thức trên sẽ ném ra ngoại lệ ClassNotFoundException. Điều này có thể bất tiện vì bạn phải sử dụng try catch hoặc ném ngoại lệ này khỏi phương thức.
Khi bạn biết chính xác tên Lớp tại thời điểm biên dịch (combine), có thể sử dụng TenLop.class để tạo đối tượng kiểu Class. Cách này đảm bảo rằng lớp được sử dụng luôn luôn tồn tại và không có ngoại lệ nào xảy ra. Đối với các kiểu dữ liệu nguyên thủy như void, int, boolean, char,… bạn có thể dùng field TYPE để lấy được đối tượng Class tương ứng.
Class c1 = Cat.class; Class c2 = int.class; Class c3 = Integer.class; Class c4 = Integer.TYPE;
Nếu bạn có một đối tượng, bạng cũng có thể lấy được đối tượng Class.
Cat cat = new Cat(); Class c = cat.getClass();
Sau đây là một ví dụ đơn giản dùng reflection để in ra các thông tin của lớp:
package com.gpcoder.reflection; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; public class ReflectClassExample { public static void main(String[] args) { try { getClassInfo(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static void getClassInfo() throws ClassNotFoundException { Class<?> aClazz = Class.forName("com.gpcoder.reflection.Cat"); System.out.println("Name: " + aClazz.getName()); System.out.println("Simple Name: " + aClazz.getSimpleName()); Package pkg = aClazz.getPackage(); System.out.println("Package Name = " + pkg.getName()); // Modifier int modifiers = aClazz.getModifiers(); boolean isPublic = Modifier.isPublic(modifiers); boolean isInterface = Modifier.isInterface(modifiers); boolean isAbstract = Modifier.isAbstract(modifiers); boolean isFinal = Modifier.isFinal(modifiers); System.out.println("Is Public? " + isPublic); // true System.out.println("Is Final? " + isFinal); // false System.out.println("Is Interface? " + isInterface); // false System.out.println("Is Abstract? " + isAbstract); // false // Lấy ra đối tượng class mô tả class cha của class Cat. Class<?> aSuperClass = aClazz.getSuperclass(); System.out.println("Simple Class Name of Super class = " + aSuperClass.getSimpleName()); // Lấy ra mảng các Class mô tả các Interface mà Cat thi hành System.out.println("\nInterface:"); Class<?>[] itfClasses = aClazz.getInterfaces(); for (Class<?> itfClass : itfClasses) { System.out.println("+ " + itfClass.getSimpleName()); } // Lấy ra danh sách các cấu tử của Cat. System.out.println("\nConstructor:"); Constructor<?>[] constructors = aClazz.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println("+ " + constructor.getName() + " has " + constructor.getParameterCount() + " param"); } // Lấy ra danh sách các method public của Cat // Bao gồm cả các method thừa kế từ class cha và các interface System.out.println("\nDeclared Methods:"); Method[] methods = aClazz.getDeclaredMethods(); for (Method method : methods) { System.out.println("+ " + method.getName()); } // Lấy ra danh sách các field public // Kể các các public field thừa kế từ các class cha, và các interface System.out.println("\nField:"); Field[] fields = aClazz.getFields(); for (Field field : fields) { System.out.println("+ " + field.getName()); } // Lấy ra danh sách các Annotation của class. System.out.println("\nAnnotation:"); Annotation[] annotations = aClazz.getAnnotations(); for (Annotation ann : annotations) { System.out.println("+ " + ann.annotationType().getSimpleName()); } } }
Kết quả thực thi chương trình trên:
Name: com.gpcoder.reflection.Cat Simple Name: Cat Package Name = com.gpcoder.reflection Is Public? true Is Final? false Is Interface? false Is Abstract? false Simple Class Name of Super class = Animal Interface: + Say Constructor: + com.gpcoder.reflection.Cat has 2 param + com.gpcoder.reflection.Cat has 1 param + com.gpcoder.reflection.Cat has 0 param Declared Methods: + getName + setName + say + getNumberOfLegs + setAge + getAge Field: + SAY + NUMBER_OF_LEGS + age Annotation: + Excel
Cấu tử (Constructor)
Lấy tất cả Constructor của một Class
Các đối tượng lớp Contructor là những phuơng thức khởi tạo của một lớp. Reflection cho phép lấy ra những Contructor từ Class Object:
Class aClazz = Cat.class; // obtain class object Constructor[] constructors = aClazz.getConstructors();
Lấy một Constructor cụ thể
Nếu như bạn biết chính xác các kiểu parameter của constructor mà bạn muốn access đến thì bạn có thể lấy về đối tượng Constructor mà mình mong muốn thay vì phải lấy tất cả (một mảng).
Class aClazz = Cat.class; // obtain Class object Constructor constructor = aClazz.getConstructor(new Class[]{String.class});
Lưu ý: Khi bạn thực hiện lấy 1 đối tượng Constructor của một lớp bất kỳ, nhưng Constructor không tồn tại thì nó sẽ quăng ra NoSuchMethodException.
Lấy danh sách tham số của một Constructor
Bạn cũng có thể truy cập được đến tham số của các Contructor, các tham số này đều được đua về kiểu Class.
Constructor constructor = ...; // obtain constructor Class[] parameterTypes = constructor.getParameterTypes();
Khởi tạo đối tượng từ đối tượng Constructor
Có hai phương thức để tạo một thể hiện của lớp:
- Class.newInstance(): tạo một đối tượng với constructor không có tham số.
- Constructor.newInstance(Object[] initargs): tạo đối tượng với constructor có tham số.
// Lấy đối tượng constructor của Cat class không có tham số Class aClazz = Cat.class; // obtain Class object Cat cat1 = (Cat) aClazz.newInstance(); // Lấy đối tượng constructor của Cat class với argument là kiểu String Constructor constructor = Cat.class.getConstructor(String.class); Cat cat2 = (Cat) constructor.newInstance("Tom");
Ví dụ
package com.gpcoder.reflection; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class ReflectConstructorExample { public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // Lấy ra đối tượng Class mô tả class Cat Class<Cat> aClass = Cat.class; // Lấy ra cấu tử có tham số (String,int) của class Cat Constructor<?> constructor = aClass.getConstructor(String.class, int.class); // Lấy ra thông tin kiểu tham số của cấu tử. System.out.println("Params:"); Class<?>[] paramClasses = constructor.getParameterTypes(); for (Class<?> paramClass : paramClasses) { System.out.println("+ " + paramClass.getSimpleName()); } // Khởi tạo đối tượng Cat theo cách thông thường. Cat tom = new Cat("Tom", 1); System.out.println("Cat 1: " + tom.getName() + ", age =" + tom.getAge()); // Khởi tạo đối tượng Cat theo cách của reflect. Cat tom2 = (Cat) constructor.newInstance("Tom", 2); System.out.println("Cat 2: " + tom.getName() + ", age =" + tom2.getAge()); } }
Kết quả thực thi chương trình trên:
Params: + String + int Cat 1: Tom, age =1 Cat 2: Tom, age =2
Lưu ý:
Bởi vì Class là một lớp Generic, bạn có thể dùng kí tự wildcard để xác định kiểu dữ liệu mà nó đại diện:
Class<?> c1 = int.class; Class<? extends Number> c2 = int.class;
Trường (Field)
Lấy các đối tượng field được khai báo là public
Bạn có thể lấy được đối tượng field được khai báo là public của một Class bằng 2 cách là chỉ lấy một field duy nhất nếu bạn biết chính xác tên của 1 field, hoặc lấy nguyên 1 mảng danh sách các field của từ một đối tượng Class.
Class aClazz = Cat.class; Field field = aClazz.getField("name"); // Tên field cần lấy // Lấy danh sách tất cả các field được khai báo là public Field[] fields = aClazz.getFields();
Lưu ý: Phương thức getField() sẽ ném ra NoSuchFieldException nếu như không tồn tại Field với tên bạn đưa vào.
Lấy các đối tượng field khai báo bất kỳ
Phương thức getField() và getFields() chỉ có thể lấy các field được khai báo là public. Vậy làm sao để access được những field được khai báo là private, protected,…? Khá đơn giản, trong Java bạn có thể lấy được chúng thông qua 2 methods là getDeclaredField() và getDeclaredFields() như sau:
Class aClazz = Cat.class; Field field = aClazz.getDeclaredField("name"); // Tên field cần lấy // Lấy danh sách tất cả các field được khai báo là public, private, ... Field[] fields = aClazz.getDeclaredFields();
Lưu ý: Các Exception có thể được ném ra khi gọi phương thức getDeclaredField():
- NoSuchFieldException : Nếu tên field bạn nhập vào không tìm thấy trong Class Cat.
- NullPointerException : Nếu tham số truyền vào là null.
- SecurityException : Nếu có Security manager được áp dụng ở lớp này.
Lấy tên field, kiểu dữ liệu kiểu field
Sau khi bạn đã có được đối tượng Field hoặc mảng Field[] bạn muốn biết tên của Field chỉ cần gọi phương thức getName() để lấy tên field, hoặc getType() để lấy kiểu dữ liệu của field.
Class<?> aClazz = Class.forName("com.gpcoder.reflection.Cat"); Field field = aClazz.getDeclaredField("name"); // Tên field cần lấy String fieldName = field.getName(); // Lấy tên field Class<?> type = field.getType(); // Lấy kiểu dữ liệu của field
Gán giá trị cho Field
Gọi phương thức Field.set(Object obj, Object value): gán value cho field tương ứng của đối tượng obj.
Ví dụ
package com.gpcoder.reflection; import java.lang.annotation.Annotation; import java.lang.reflect.Field; public class ReflectFieldExample { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { // Lấy ra đối tượng Class mô tả class Cat Class<Cat> aClazz = Cat.class; // Lấy ra danh sách các field public // Kể các các public field thừa kế từ các class cha, và các interface System.out.println("Field:"); Field[] fields = aClazz.getFields(); for (Field field : fields) { System.out.println("+ " + field.getName()); } // Lấy ra field có tên 'NUMBER_OF_LEGS': Field field = aClazz.getField("NUMBER_OF_LEGS"); // Ghi ra kiểu của Field Class<?> fieldType = field.getType(); System.out.println("Field type: " + fieldType.getSimpleName()); // Khởi tạo đối tượng Cat Cat tom = new Cat("Tom", 1); // Lấy ra giá trị của trường "age" theo cách của Reflect. Field ageField = aClazz.getField("age"); Integer age = (Integer) ageField.get(tom); System.out.println("Age = " + age); // Gán giá trị mới cho trường "age". ageField.set(tom, 2); System.out.println("New Age = " + tom.getAge()); // Lấy ra danh sách các Annotation của field. System.out.println("\nAnnotation:"); Annotation[] annotations = ageField.getAnnotations(); for (Annotation ann : annotations) { System.out.println("+ " + ann.annotationType().getSimpleName()); } } }
Kết quả thực thi chương trình trên:
Field: + SAY + NUMBER_OF_LEGS + age Field type: int Age = 1 New Age = 2 Annotation: + ExcelColumn
Phương thức (method)
Như đã nói ở trước đó, khi bạn sử dụng java reflection bạn có thể truy cập vào các phuơng thức của một lớp và gọi những phuơng thức này.
Lấy tất cả Method của một Class
Phương thức Class.getMethods(): trả về danh sách đối tượng Method của một lớp.
Class aClazz = Cat.class; // obtain class object Method[] methods = aClazz.getMethods();
Lấy một Method cụ thể
Phương thức Class.getMethod(String name, Class[] parameterTypes): trả về đối tượng Method đại diện cho một phương thức của lớp. Phương thức này được xác định qua tên và các kiểu tham số.
Class aClazz = Cat.class; // obtain class object Method method = aClazz.getMethod("setName", String.class); // get method that takes a String as argument
Thực thi một Method
Phương thức Method.invoke(Object obj, Object[] args) thực thi phương thức tương ứng của đối tượng obj với các tham số args.
Ví dụ
Ví dụ lấy ra một method cho bởi tên, và các tham số chỉ định trước. Ghi ra thông tin về method này, như kiểu trả về, danh sách các tham số,…
package com.gpcoder.reflection; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectMethodExample { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // Lấy ra đối tượng Class mô tả class Cat Class<Cat> aClazz = Cat.class; // Lấy ra danh sách các method public của Cat // Bao gồm cả các method thừa kế từ class cha và các interface System.out.println("Declared Methods:"); Method[] methods = aClazz.getDeclaredMethods(); for (Method method : methods) { System.out.println("+ " + method.getName()); } // Lấy ra đối tượng 'Method' mô tả method getAge() Method getAgeMethod = aClazz.getMethod("getAge"); // Kiểu trả về của method getAge Class<?> returnType = getAgeMethod.getReturnType(); System.out.println("Return type of getAge: " + returnType.getSimpleName()); Cat tom = new Cat("Tom", 1); // Gọi method 'getAge' theo cách của Reflect // Nó tương đương với gọi: tom.getAge() int age = (int) getAgeMethod.invoke(tom); System.out.println("Age = " + age); // Lấy ra đối tượng 'Method' mô tả method setAge(int) của class Cat. Method setAgeMethod = aClazz.getMethod("setAge", int.class); // Gọi method setAge(int) theo cách của Reflect. // Nó tương đương với gọi: tom.setAge(2); setAgeMethod.invoke(tom, 2); System.out.println("New Age = " + tom.getAge()); } }
Kết quả thực thi chương trình trên:
Declared Methods: + getName + setName + getNumberOfLegs + setAge + getAge + say Return type of getAge: int Age = 1 New Age = 2
Ví dụ dưới đây, liệt kê ra các phương thức public setter, và các public getter của class.
package com.gpcoder.reflection; import java.lang.reflect.Method; public class ReflectMethodExample2 { public static void main(String[] args) { // Lấy ra đối tượng Class mô tả class Cat Class<Cat> aClazz = Cat.class; // Lấy ra danh sách các public method. Method[] methods = aClazz.getMethods(); for (Method method : methods) { System.out.println("Method: " + method.getName()); System.out.println(" - Is Setter? " + isSetter(method)); System.out.println(" - Is Getter? " + isGetter(method)); } } // Method là getter nếu có tên bắt đầu bằng get, và không có tham số. public static boolean isGetter(Method method) { if (!method.getName().startsWith("get")) { return false; } if (method.getParameterTypes().length != 0) { return false; } if (void.class.equals(method.getReturnType())) { return false; } return true; } // Method là setter nếu có tên bắt đầu bằng set, và chỉ có 1 tham số. public static boolean isSetter(Method method) { if (!method.getName().startsWith("set")) { return false; } if (method.getParameterTypes().length != 1) { return false; } return true; } }
Kết quả thực thi chương trình trên:
Method: getName - Is Setter? false - Is Getter? true Method: getNumberOfLegs - Is Setter? false - Is Getter? true Method: setAge - Is Setter? true - Is Getter? false Method: say - Is Setter? false - Is Getter? false Method: getAge - Is Setter? false - Is Getter? true Method: getLocation - Is Setter? false - Is Getter? true Method: wait - Is Setter? false - Is Getter? false Method: wait - Is Setter? false - Is Getter? false Method: wait - Is Setter? false - Is Getter? false Method: equals - Is Setter? false - Is Getter? false Method: toString - Is Setter? false - Is Getter? false Method: hashCode - Is Setter? false - Is Getter? false Method: getClass - Is Setter? false - Is Getter? true Method: notify - Is Setter? false - Is Getter? false Method: notifyAll - Is Setter? false - Is Getter? false
Truy cập vào các private method, field
Bạn không thể truy cập vào các method hay field mà nó là private theo cách thông thường, quá trình biên dịch java cũng không cho phép điều đó. Nhưng với Java Reflection điều đó hoàn toàn có thể.
Ví dụ truy cập vào một private field.
package com.gpcoder.reflection; import java.lang.reflect.Field; public class AccessPrivateFieldExample { public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { // Tạo một đối tượng Class mô tả class Cat. Class<Cat> aClazz = Cat.class; // Class.getField(String) chỉ lấy được các trường public. // Sử dụng Class.getDeclaredField(String): // Lấy ra đối tượng Field mô tả trường name của class Cat. // (Trường khi báo trong class này). Field private_nameField = aClazz.getDeclaredField("name"); // Cho phép để truy cập vào các trường private. // Nếu không sẽ bị ngoại lệ IllegalAccessException private_nameField.setAccessible(true); Cat tom = new Cat("Tom"); String fieldValue = (String) private_nameField.get(tom); System.out.println("Value field name = " + fieldValue); // Sét đặt trường name giá trị mới. private_nameField.set(tom, "Tom Cat"); System.out.println("New name = " + tom.getName()); } }
Kết quả thực thi chương trình trên:
Value field name = Tom New name = Tom Cat
Ví dụ truy cập vào một private method.
package com.gpcoder.reflection; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class AccessPrivateMethodExample { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // Tạo một đối tượng Class mô tả class Cat. Class<Cat> aClass = Cat.class; // Class.getMethod(String) chỉ lấy được các method public. // Sử dụng Class.getDeclaredMethod(String): // Lấy ra đối tượng Method mô tả method setName(String) của class Cat. // (Phương thức khai báo trong class). Method private_setNameMethod = aClass.getDeclaredMethod("setName", String.class); // Cho phép để truy cập vào các method private. // Nếu không sẽ bị ngoại lệ IllegalAccessException private_setNameMethod.setAccessible(true); Cat tom = new Cat("Tom"); // Gọi private method. private_setNameMethod.invoke(tom, "Tom Cat"); System.out.println("New name = " + tom.getName()); } }
Kết quả thực thi chương trình trên:
New name = Tom Cat
Thay đổi giá trị field có khai báo là static final
package com.gpcoder.reflection; import java.lang.reflect.Field; import java.lang.reflect.Modifier; public class ChangeStaticFinalFieldExample { public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { // create Cat Cat cat = new Cat(); // Get field instance Field field = cat.getClass().getDeclaredField("NUMBER_OF_LEGS"); field.setAccessible(true); // Suppress Java language access checking // Remove "final" modifier Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); // Get value Integer fieldValue = (Integer) field.get(null); System.out.println(cat.getNumberOfLegs()); // -> 4 // Set value field.set(null, 2); System.out.println(cat.getNumberOfLegs()); // -> 2 } }
Chú thích (Annotation)
Bạn có thể sử dụng Reflection để truy cập (access) các Annotations được áp dụng trong các class tại thời điểm Runtime.
Ví dụ tạo một Annnotation như sau:
package com.gpcoder.reflection; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; // Annotation này có thể sử dụng tại thời điểm chạy (Runtime) của chương trình. @Retention(RetentionPolicy.RUNTIME) // Có thể dùng cho class,interface, method, field, parameter. @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) public @interface MyAnnotation { String name(); String value() default "Defaul value"; }
Một ví dụ Annotation với class
package com.gpcoder.reflection; import java.lang.annotation.Annotation; @MyAnnotation(name = "Table", value = "Employee") public class ClassAnnotationExample { public static void main(String[] args) { Class<?> aClazz = ClassAnnotationExample.class; // Lấy ra danh sách các Annotation của class. Annotation[] annotations = aClazz.getAnnotations(); for (Annotation ann : annotations) { System.out.println("Annotation: " + ann.annotationType().getSimpleName()); } // Hoặc lấy cụ thể. Annotation ann = aClazz.getAnnotation(MyAnnotation.class); MyAnnotation myAnn = (MyAnnotation) ann; System.out.println("Name = " + myAnn.name()); System.out.println("Value = " + myAnn.value()); } }
Kết quả thực thi chương trình trên:
Annotation: MyAnnotation Name = Table Value = Employee
Ví dụ Annotation với Field và Method
package com.gpcoder.reflection; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; public class FieldMethodAnnotationExample { @MyAnnotation(name = "My Field") private int myField; @MyAnnotation(name = "My Method", value = "My Method Value") protected void myMethod(String str) { } public static void main(String[] args) throws NoSuchFieldException, SecurityException, NoSuchMethodException { // Lấy đối tượng Class Class<?> aClazz = FieldMethodAnnotationExample.class; // Lấy đối tượng Field System.out.println(" == FIELD == "); Field field = aClazz.getDeclaredField("myField"); // Lấy ra danh sách các Annotation của field. Annotation[] fieldAnns = field.getAnnotations(); for (Annotation methodAnn : fieldAnns) { System.out.println("Annotation: " + methodAnn.annotationType().getSimpleName()); } // Lấy cụ thể. Annotation fieldAnn = field.getAnnotation(MyAnnotation.class); MyAnnotation myAnn1 = (MyAnnotation) fieldAnn; System.out.println("Name = " + myAnn1.name()); System.out.println("Value = " + myAnn1.value()); // Tương tự với method ... System.out.println(" == METHOD == "); Method method = aClazz.getDeclaredMethod("myMethod", String.class); // Lấy ra danh sách các Annotation của method. Annotation[] methodAnns = method.getAnnotations(); for (Annotation methodAnn : methodAnns) { System.out.println("Annotation: " + methodAnn.annotationType().getSimpleName()); } // Lấy cụ thể. Annotation methodAnn = method.getAnnotation(MyAnnotation.class); MyAnnotation myAnn2 = (MyAnnotation) methodAnn; System.out.println("Name = " + myAnn2.name()); System.out.println("Value = " + myAnn2.value()); } }
Kết quả thực thi chương trình trên:
== FIELD == Annotation: MyAnnotation Name = My Field Value = Defaul value == METHOD == Annotation: MyAnnotation Name = My Method Value = My Method Value
Ví dụ Annotation với tham số của method
package com.gpcoder.reflection; import java.lang.annotation.Annotation; import java.lang.reflect.Method; public class ParameterAnnotationExample { // Ví dụ một method có Annotation ở tham số. protected void doSomething(int jobType, @MyAnnotation(name = "Table", value = "Employee") String info) { } public static void main(String[] args) throws NoSuchMethodException, SecurityException { // Lấy đối tượng Class Class<?> aClass = ParameterAnnotationExample.class; // Lấy ra đối tượng Method của method doSomething(int,String) Method method = aClass.getDeclaredMethod("doSomething", int.class, String.class); // Lấy ra danh sách các Parameter của method. Class<?>[] parameterTypes = method.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println("Parametete Type: " + parameterType.getSimpleName()); } System.out.println(" ---- "); // Lấy ra mảng 2 chiều các Annotation trong các Parameter. Annotation[][] annotationss = method.getParameterAnnotations(); // Lấy ra danh sách các Annotation của Parameter tại vị trí Index =1. Annotation[] annotations = annotationss[1]; for (Annotation ann : annotations) { System.out.println("Annotation: " + ann.annotationType().getSimpleName()); } } }
Kết quả thực thi chương trình trên:
Parametete Type: int Parametete Type: String ---- Annotation: MyAnnotation
Tài liệu tham khảo:
- https://docs.oracle.com/javase/tutorial/reflect/
- http://o7planning.org/vi/10155/huong-dan-su-dung-java-reflection
- https://yinyangit.wordpress.com/2011/11/12/java-tim-hieu-ve-reflection-runtime-type-information/
- http://java-performance.info/updating-final-and-static-final-fields/