Cũng có thể thêm chú thích vào một lớp Java khi chạy bằng cách sử dụng API phản chiếu Java. Về cơ bản người ta phải tạo lại các bản đồ chú thích nội bộ được định nghĩa trong lớp java.lang.Class
(hoặc đối với Java 8 được định nghĩa trong lớp nội bộ java.lang.Class.AnnotationData
). Đương nhiên, cách tiếp cận này khá khó khăn và có thể phá vỡ bất cứ lúc nào cho các phiên bản Java mới hơn. Nhưng để thử nghiệm/tạo mẫu nhanh chóng và bẩn thì phương pháp này có thể hữu ích vào những thời điểm.
proove của khái niệm ví dụ cho Java 8:
public final class RuntimeAnnotations {
private static final Constructor<?> AnnotationInvocationHandler_constructor;
private static final Constructor<?> AnnotationData_constructor;
private static final Method Class_annotationData;
private static final Field Class_classRedefinedCount;
private static final Field AnnotationData_annotations;
private static final Field AnnotationData_declaredAnotations;
private static final Method Atomic_casAnnotationData;
private static final Class<?> Atomic_class;
static{
// static initialization of necessary reflection Objects
try {
Class<?> AnnotationInvocationHandler_class = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
AnnotationInvocationHandler_constructor = AnnotationInvocationHandler_class.getDeclaredConstructor(new Class[]{Class.class, Map.class});
AnnotationInvocationHandler_constructor.setAccessible(true);
Atomic_class = Class.forName("java.lang.Class$Atomic");
Class<?> AnnotationData_class = Class.forName("java.lang.Class$AnnotationData");
AnnotationData_constructor = AnnotationData_class.getDeclaredConstructor(new Class[]{Map.class, Map.class, int.class});
AnnotationData_constructor.setAccessible(true);
Class_annotationData = Class.class.getDeclaredMethod("annotationData");
Class_annotationData.setAccessible(true);
Class_classRedefinedCount= Class.class.getDeclaredField("classRedefinedCount");
Class_classRedefinedCount.setAccessible(true);
AnnotationData_annotations = AnnotationData_class.getDeclaredField("annotations");
AnnotationData_annotations.setAccessible(true);
AnnotationData_declaredAnotations = AnnotationData_class.getDeclaredField("declaredAnnotations");
AnnotationData_declaredAnotations.setAccessible(true);
Atomic_casAnnotationData = Atomic_class.getDeclaredMethod("casAnnotationData", Class.class, AnnotationData_class, AnnotationData_class);
Atomic_casAnnotationData.setAccessible(true);
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException | NoSuchFieldException e) {
throw new IllegalStateException(e);
}
}
public static <T extends Annotation> void putAnnotation(Class<?> c, Class<T> annotationClass, Map<String, Object> valuesMap){
putAnnotation(c, annotationClass, annotationForMap(annotationClass, valuesMap));
}
public static <T extends Annotation> void putAnnotation(Class<?> c, Class<T> annotationClass, T annotation){
try {
while (true) { // retry loop
int classRedefinedCount = Class_classRedefinedCount.getInt(c);
Object /*AnnotationData*/ annotationData = Class_annotationData.invoke(c);
// null or stale annotationData -> optimistically create new instance
Object newAnnotationData = createAnnotationData(c, annotationData, annotationClass, annotation, classRedefinedCount);
// try to install it
if ((boolean) Atomic_casAnnotationData.invoke(Atomic_class, c, annotationData, newAnnotationData)) {
// successfully installed new AnnotationData
break;
}
}
} catch(IllegalArgumentException | IllegalAccessException | InvocationTargetException | InstantiationException e){
throw new IllegalStateException(e);
}
}
@SuppressWarnings("unchecked")
private static <T extends Annotation> Object /*AnnotationData*/ createAnnotationData(Class<?> c, Object /*AnnotationData*/ annotationData, Class<T> annotationClass, T annotation, int classRedefinedCount) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) AnnotationData_annotations.get(annotationData);
Map<Class<? extends Annotation>, Annotation> declaredAnnotations= (Map<Class<? extends Annotation>, Annotation>) AnnotationData_declaredAnotations.get(annotationData);
Map<Class<? extends Annotation>, Annotation> newDeclaredAnnotations = new LinkedHashMap<>(annotations);
newDeclaredAnnotations.put(annotationClass, annotation);
Map<Class<? extends Annotation>, Annotation> newAnnotations ;
if (declaredAnnotations == annotations) {
newAnnotations = newDeclaredAnnotations;
} else{
newAnnotations = new LinkedHashMap<>(annotations);
newAnnotations.put(annotationClass, annotation);
}
return AnnotationData_constructor.newInstance(newAnnotations, newDeclaredAnnotations, classRedefinedCount);
}
@SuppressWarnings("unchecked")
public static <T extends Annotation> T annotationForMap(final Class<T> annotationClass, final Map<String, Object> valuesMap){
return (T)AccessController.doPrivileged(new PrivilegedAction<Annotation>(){
public Annotation run(){
InvocationHandler handler;
try {
handler = (InvocationHandler) AnnotationInvocationHandler_constructor.newInstance(annotationClass,new HashMap<>(valuesMap));
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
return (Annotation)Proxy.newProxyInstance(annotationClass.getClassLoader(), new Class[] { annotationClass }, handler);
}
});
}
}
Cách sử dụng Ví dụ:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TestAnnotation {
String value();
}
public static class TestClass{}
public static void main(String[] args) {
TestAnnotation annotation = TestClass.class.getAnnotation(TestAnnotation.class);
System.out.println("TestClass annotation before:" + annotation);
Map<String, Object> valuesMap = new HashMap<>();
valuesMap.put("value", "some String");
RuntimeAnnotations.putAnnotation(TestClass.class, TestAnnotation.class, valuesMap);
annotation = TestClass.class.getAnnotation(TestAnnotation.class);
System.out.println("TestClass annotation after:" + annotation);
}
Output:
TestClass annotation before:null
TestClass annotation after:@RuntimeAnnotations$TestAnnotation(value=some String)
Hạn chế của phương pháp này:
- Phiên bản Java mới có thể phá vỡ mã bất kỳ lúc nào.
- Ví dụ trên chỉ hoạt động cho Java 8 - làm cho nó hoạt động cho các phiên bản Java cũ hơn sẽ yêu cầu kiểm tra phiên bản Java khi chạy và thay đổi việc triển khai tương ứng.
- Nếu Lớp được chú thích nhận được redefined (ví dụ: trong khi gỡ lỗi), chú thích sẽ bị mất.
- Không được kiểm tra kỹ lưỡng; không chắc chắn nếu có bất kỳ tác dụng phụ xấu - sử dụng có nguy cơ của riêng bạn ...
Kiểm tra này có thể được giúp bạn http://stackoverflow.com/a/14276270/4741746 ít nhất chúng ta có thể sửa đổi nó –