Adding Java Annotations at Run Time

Is it possible to add annotation to an object (in my case, in particular, to a method) at runtime?

A bit more explanation: I have two modules, moduleA and moduleB. module B depends on module A, which is independent of nothing. (modA is my main data types and interfaces, and, for example, modB is db / data layer) modB is also dependent on externalLibrary. In my case, modB passes the class from modA to externalLibrary, which requires specific methods for annotation. Concrete annotations are all part of externalLib, and, as I said, modA is independent of externalLib, and I would like to keep it that way.

So, is this possible, or do you have suggestions for other ways to address this issue?

+59
java annotations runtime
Oct 28 '09 at 3:56
source share
4 answers

It is not possible to add annotation at runtime, it seems you need to enter an adapter , which is used by module B to wrap an object from module A that exposes the required annotated methods.

+21
Oct 28 '09 at 4:00
source share
β€” -

This is possible through a bytecode tool library such as Javassist .

In particular, take a look at the AnnotationsAttribute class for an example on how to create / set annotations and the bytecode API manual section for general guidelines on managing class files.

This is nothing but simple and simple, although I would not recommend this approach and suggest you instead answer the question if you do not need to do this for a huge number of classes (or the specified classes are not available to you until runtime and, therefore, writing an adapter impossible).

+37
Oct 28 '09 at 4:15
source share

You can also add annotation to the Java class at run time using the Java reflection API. Essentially, you need to recreate the internal annotation maps defined in the java.lang.Class class (or for Java 8, defined in the internal java.lang.Class.AnnotationData class). Naturally, this approach is pretty hacky and can break at any time for new versions of Java. But for quick and dirty testing / prototyping, this approach can be useful from time to time.

An example of a conceptual example for 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); } }); } } 

Usage example:

 @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) 

Limitations of this approach:

  • Newer versions of Java can break code at any time.
  • The above example works only for Java 8 - to work with older versions of Java, you will need to check the Java version at run time and, accordingly, change the implementation.
  • If the annotated class gets redefined (for example, during debugging), the annotation will be lost.
  • Not fully verified; not sure if there are any bad side effects - use at your own risk ...
+11
May 17 '15 at 1:10
source share

You can create annotations at runtime through Proxy . You can then add them to your Java objects via reflection, as suggested in other answers (but you probably would be better off finding an alternative way to handle this, as messing up existing types through reflection can be dangerous and hard to debug).

But it is not very simple ... I wrote a library that I named, hope that Javanna just make it easy using a clean API.

At JCenter and Maven Central .

Using it:

 @Retention( RetentionPolicy.RUNTIME ) @interface Simple { String value(); } Simple simple = Javanna.createAnnotation( Simple.class, new HashMap<String, Object>() {{ put( "value", "the-simple-one" ); }} ); 

If any map entry does not match the declared annotation fields and type (s), an Exception is thrown. If any value that does not have a default value is absent, an exception is thrown.

This suggests that each instance of the annotation that was created successfully is safe to use as an instance of the compilation time annotation.

As a bonus, this library can also analyze annotation classes and return annotation values ​​in the form of a map:

 Map<String, Object> values = Javanna.getAnnotationValues( annotation ); 

This is useful for creating mini-frameworks.

0
Nov 25 '16 at 20:40
source share



All Articles