我想提供带有某些方法生成的某些值的注释。
到目前为止我试过这个:
public @interface MyInterface {
String aString();
}
@MyInterface(aString = MyClass.GENERIC_GENERATED_NAME)
public class MyClass {
static final String GENERIC_GENERATED_NAME = MyClass.generateName(MyClass.class);
public static final String generateName(final Class<?> c) {
return c.getClass().getName();
}
}
思想GENERIC_GENERATED_NAME
是static final
,它抱怨
注释属性
MyInterface.aString
的值必须是常量表达式
那么如何实现这个目标呢?
答案 0 :(得分:19)
无法动态生成注释中使用的字符串。编译器在编译时评估RetentionPolicy.RUNTIME
注释的注释元数据,但直到运行时才知道GENERIC_GENERATED_NAME
。并且您不能将生成的值用于RetentionPolicy.SOURCE
注释,因为它们在编译时被丢弃,因此这些生成的值将永远不知道。
答案 1 :(得分:6)
解决方案是使用带注释的方法。调用该方法(使用反射)来获取动态值。
从用户的角度来看,我们有:
@MyInterface
public class MyClass {
@MyName
public String generateName() {
return MyClass.class.getName();
}
}
注释本身将定义为
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface @MyName {
}
实现这两个注释的查找非常简单。
// as looked up by @MyInterface
Class<?> clazz;
Method[] methods = clazz.getDeclaredMethods();
if (methods.length != 1) {
// error
}
Method method = methods[0];
if (!method.isAnnotationPresent(MyName.class)) {
// error as well
}
// This works if the class has a public empty constructor
// (otherwise, get constructor & use setAccessible(true))
Object instance = clazz.newInstance();
// the dynamic value is here:
String name = (String) method.invoke(instance);
答案 2 :(得分:0)
无法像其他人所说的那样动态修改注释的属性。如果你想实现这一目标,有两种方法可以做到这一点。
将表达式分配给注释中的属性,并在检索注释时处理该表达式。在您的情况下,您的注释可以是
@MyInterface(aString =“objectA.doSomething(args1,args2)”)
当您阅读它时,您可以处理字符串并进行方法调用并检索值。 Spring通过SPEL(Spring表达式语言)来做到这一点。这是资源密集型的,每次我们想要处理表达式时都会浪费cpu周期。如果您使用的是spring,则可以挂钩beanPostProcessor并处理表达式并将结果存储在某处。 (全局属性对象或可在任何地方检索的地图)。
jdk存储注释图的方式是依赖于Java版本的,并且不可靠,因为它没有公开使用(它是私有的)。
您可以在此处找到参考实现。
https://rationaleemotions.wordpress.com/2016/05/27/changing-annotation-values-at-runtime/
P.S:我没有尝试过第二种测试方法。