起初一看,我印象中是不行的,因为我一直以为XXX.class.getAnnotation(XXXAnnotation.class)这个方法是一个native方法
直到刚自己去看了一眼,哎呀,不是native方法啊
好家伙,源来氏Class类中有一个私有内部静态类AnnotationData负责存储Class的注解里的值(前提你的注解是RetentionPolicy.RUNTIME的),当然存法也很简单就是用一个map存储了注解类型和注解实例
所以看起来直接去修改对应Class的AnnotationData属性中的annotations map里的注解实例就可以了
但是其实不然,因为我们反射是拿不到反射相关类的属性的,例如Class,Field这些,不然那可不乱了套了(相当于搁这搁这这种套娃操作了...),因此呢只能再换个思路
我们可以注意到,虽然注解实例缓存在Class中,但是它确实可以通过getAnnotation获取到对应的实例,而这个实例恰好是一个代理对象,其实也就是用咱们jdk动态代理做的,只用debug一看就很清楚了
既然是代理对象,那肯定有对应的InvocationHandler啊,既然代理对象能够实现原注解的所有功能,那意味着其对应的InvocationHandler肯定也包含了该注解的所有功能。
果不其然,注解对应InvocationHandler是AnnotationInvocationHandler,这个类就很憨厚了,其中的memberValues属性就是我们需要的,怎么证实呢?
比如我们有个注解TestAnnotation@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TestAnnotation {
String value();
}
注解到一个叫Test的类上,value值就写成haha
我们直接通过getAnnotation拿到对应的注解,再用Proxy.getInvocationHandler拿到对应的InvocationHandlerTestAnnotation testAnnotation = Test.class.getDeclaredAnnotation(TestAnnotation.class);
InvocationHandler invocationHandler = Proxy.getInvocationHandler(testAnnotation);
这个debug一看就很明显啦
memberValues中就藏着我们的目标,所以我们可以直接通过反射修改它就可以了,比如这样TestAnnotation annotation = Test.class.getAnnotation(TestAnnotation.class);
System.out.println("before value: " + annotation.value());
InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
Field value = invocationHandler.getClass().getDeclaredField("memberValues");
value.setAccessible(true);
Map memberValues = (Map) value.get(invocationHandler);
memberValues.put("value", "i am new value");
annotation = Test.class.getAnnotation(TestAnnotation.class);
System.out.println("after value: " + annotation.value());
最终打印效果:
但是这样有个问题,什么呢?就是注解不仅仅是可以注解在类上的,也可以注解在字段上,方法上,所以我后面看了一下字段和方法上是怎么实现这个getAnnotation,那肯定就不是用AnnotationData啦,毕竟人家是Class的内部类
Field呢是内部有个私有declaredAnnotations属性,是Map, Annotation>的类型,那这个就和AnnotationData里的annotations类似啦
而Method是其父类Executable有个declaredAnnotations属性,还是Map, Annotation>类型
那说明无论是怎么获取注解,都是从某个地方的Map, Annotation>缓存中获取。那怎么把不同地方,例如类,方法,字段聚合在一个工具类中呢?
铛铛铛!恰好有个顶层的接口把它们聚在了一起AnnotatedElement。因此工具方法应运而生(其实他们的getAnnotation方法就是来自AnnotatedElement接口的方法)public static void modify(AnnotatedElement element,
Class extends Annotation> annotationClass,
String key, Object value) throws NoSuchFieldException, IllegalAccessException {
Annotation annotationToBeModified = element.getAnnotation(annotationClass);
if (annotationToBeModified == null) return;
InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotationToBeModified);
Field memberValuesField = invocationHandler.getClass().getDeclaredField("memberValues");
memberValuesField.setAccessible(true);
Map memberValues = (Map) memberValuesField.get(invocationHandler);
memberValues.put(key, value);
}
参数也很好理解,AnnotatedElement就是被注解的地方啦,如果呢你是注解的类,就传Class,注解的方法,就传某个Method,注解的是字段,就传Field,就算注解的参数,那就传某个Parameter,总之理论上啥都够啦
最后题主提到的MySQL那就不是大问题啦,毕竟它只是一个数据库而已,如果真有某个工具可以实现题主的需求,那提供这个工具的人也太嘞了。。。竟然必须要MySQL,所以啊,这个问题最大的点应该还是如何实现运行时修改注解里的值,不过最后实现下来看起来也没多少代码,只是一个小工具,可以放到自己项目中进行一些组装成为其他业务组件的一个部分吧。
那就酱。。。o( ̄▽ ̄)d