Imagine there is a class:
@Something(someProperty = "some value") public class Foobar { //... }
Which is already compiled (I cannot control the source), and is part of the classpath when the jvm starts up. I would like to be able to change "some value" to something else at runtime, such that any reflection thereafter would have my new value instead of the default "some value".
Is this possible? If so, how?
Alter Annotation Java class Class maintains a map for managing annotations – Annotation class as keys and Annotation object as value: Map<Class<? extends Annotation>, Annotation> map; We will update this map to alter annotation at runtime.
Annotations require constant values and a method parameter is dynamic. Show activity on this post. Annotation Usage: @CacheClear(pathToVersionId = "[0]") public int importByVersionId(Long versionTo){ ...... }
The @interface element is used to declare an annotation. For example: @interface MyAnnotation{}
Warning: Not tested on OSX - see comment from @Marcel
Tested on OSX. Works fine.
Since I also had the need to change annotation values at runtime, I revisited this question.
Here is a modified version of @assylias approach (many thanks for the inspiration).
/** * Changes the annotation value for the given key of the given annotation to newValue and returns * the previous value. */ @SuppressWarnings("unchecked") public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue){ Object handler = Proxy.getInvocationHandler(annotation); Field f; try { f = handler.getClass().getDeclaredField("memberValues"); } catch (NoSuchFieldException | SecurityException e) { throw new IllegalStateException(e); } f.setAccessible(true); Map<String, Object> memberValues; try { memberValues = (Map<String, Object>) f.get(handler); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } Object oldValue = memberValues.get(key); if (oldValue == null || oldValue.getClass() != newValue.getClass()) { throw new IllegalArgumentException(); } memberValues.put(key,newValue); return oldValue; }
Usage example:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ClassAnnotation { String value() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface FieldAnnotation { String value() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MethodAnnotation { String value() default ""; } @ClassAnnotation("class test") public static class TestClass{ @FieldAnnotation("field test") public Object field; @MethodAnnotation("method test") public void method(){ } } public static void main(String[] args) throws Exception { final ClassAnnotation classAnnotation = TestClass.class.getAnnotation(ClassAnnotation.class); System.out.println("old ClassAnnotation = " + classAnnotation.value()); changeAnnotationValue(classAnnotation, "value", "another class annotation value"); System.out.println("modified ClassAnnotation = " + classAnnotation.value()); Field field = TestClass.class.getField("field"); final FieldAnnotation fieldAnnotation = field.getAnnotation(FieldAnnotation.class); System.out.println("old FieldAnnotation = " + fieldAnnotation.value()); changeAnnotationValue(fieldAnnotation, "value", "another field annotation value"); System.out.println("modified FieldAnnotation = " + fieldAnnotation.value()); Method method = TestClass.class.getMethod("method"); final MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class); System.out.println("old MethodAnnotation = " + methodAnnotation.value()); changeAnnotationValue(methodAnnotation, "value", "another method annotation value"); System.out.println("modified MethodAnnotation = " + methodAnnotation.value()); }
The advantage of this approach is, that one does not need to create a new annotation instance. Therefore one doesn't need to know the concrete annotation class in advance. Also the side effects should be minimal since the original annotation instance stays untouched.
Tested with Java 8.
This code does more or less what you ask for - it is a simple proof of concept:
declaredAnnotations
Output:
oldAnnotation = some value
modifiedAnnotation = another value
public static void main(String[] args) throws Exception { final Something oldAnnotation = (Something) Foobar.class.getAnnotations()[0]; System.out.println("oldAnnotation = " + oldAnnotation.someProperty()); Annotation newAnnotation = new Something() { @Override public String someProperty() { return "another value"; } @Override public Class<? extends Annotation> annotationType() { return oldAnnotation.annotationType(); } }; Field field = Class.class.getDeclaredField("annotations"); field.setAccessible(true); Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) field.get(Foobar.class); annotations.put(Something.class, newAnnotation); Something modifiedAnnotation = (Something) Foobar.class.getAnnotations()[0]; System.out.println("modifiedAnnotation = " + modifiedAnnotation.someProperty()); } @Something(someProperty = "some value") public static class Foobar { } @Retention(RetentionPolicy.RUNTIME) @interface Something { String someProperty(); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With