我认为标题描述了这个问题。这是一些代码:
import static org.junit.Assert.assertEquals;
import java.lang.annotation.*;
public class Main {
public static void main(String[] args) throws Exception {
assertEquals("foo", Main.class.getDeclaredMethod("myMethod").getAnnotation(Anno.class).param());
// the magic here -> set to bar
assertEquals("bar", Main.class.getDeclaredMethod("myMethod").getAnnotation(Anno.class).param());
}
@Anno(param = "foo")
public void myMethod() {}
@Retention(RetentionPolicy.RUNTIME)
@interface Anno {
String param();
}
}
到目前为止,我猜这是不可能的。似乎总是当你试图通过反射获得一个方法时,你只能获得副本,所有的值(如注释)都会从更深层的java层重新读取。在这些副本中,您可以更改值,但如果重新加载则这些值将消失。
我错过了什么或者它真的不可能吗?
答案 0 :(得分:3)
注释是修饰符,就像private
或synchronized
一样。它们是类的不变静态结构的一部分,并不打算进行修改。您可以入侵Reflection实现,使特定方法打印出您想要的内容,但除了黑客的肮脏之外,您只是没有更改注释,只是破解了特定库的数据结构。
还有其他反射或字节代码操作库,它们不使用内置的Reflection API,而是直接读取字节代码(例如通过getResource()
或通过Instrumentation API)。这些库永远不会注意到你的操作。
进一步注意,由于这些值应该是嵌入在类文件中的常量,因此Reflection实现总是可以使用延迟提取加上根据无法控制的条件删除缓存的值,因为这些值总是可以被重新获得。也没有保证Reflection实现完全使用数据结构;它还可以生成返回常量值的代码。
换句话说,如果要将可变数据与方法关联,请不要使用注释。您可以简单地使用Map<Method,MutableData>
,或者,如果您只有一个特定的方法,则声明一个好的旧static
字段,它已经提供了您需要的所有功能,并且更容易处理。
答案 1 :(得分:1)
我发布了一个在Java 8之前执行相同任务的链接。似乎可以在Java 8(1.8.0_51)中执行相同的操作。整个测试设置
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
public class Test {
public static void main(String... args) {
Test t = new Test();
t.check();
Test.change();
t.check();
new Test().check();
}
public Test() {
}
public static void change() {
try {
Method m = Test.class.getMethod("myMethod");
// The map has to be built for the first time
m.getDeclaredAnnotations();
Class<?> superclass = m.getClass().getSuperclass();
Field declaredField = superclass.getDeclaredField("declaredAnnotations");
declaredField.setAccessible(true);
@SuppressWarnings("unchecked")
Map<Class<? extends Annotation>, Annotation> map = (Map<Class<? extends Annotation>, Annotation>) declaredField
.get(m);
map.put(Anno.class, new Anno() {
@Override
public Class<? extends Annotation> annotationType() {
return Anno.class;
}
@Override
public String param() {
return "new";
}
});
} catch (SecurityException | NoSuchMethodException | IllegalArgumentException | NoSuchFieldException
| IllegalAccessException e) {
e.printStackTrace();
}
}
public void check() {
try {
System.out.println(getClass().getMethod("myMethod").getAnnotation(Anno.class).param());
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
@Anno(param = "test")
public void myMethod() {
}
}
@Retention(RetentionPolicy.RUNTIME)
@interface Anno {
String param();
}