在运行时添加Java注释

是否有可能在运行时添加一个注解到一个对象(在我的情况下,特别是一个方法)?

对于更多的解释:我有两个模块,moduleA和moduleB。 moduleB取决于moduleA,它不依赖于任何东西。 (modA是我的核心数据types和接口等等,modB是db / data层)modB也依赖于externalLibrary。 在我的情况下,modB将一个类从modA移交给externalLibrary,这需要对某些方法进行注释。 具体的注释都是externalLib的一部分,正如我所说,modA不依赖于externalLib,我想保持这种方式。

所以,这是可能的,或者你有其他方法来看待这个问题的build议吗?

在运行时不可能添加注释,这听起来像是需要引入一个适配器 ,模块B用它来包装来自模块A的对象,暴露所需的注释方法。

这可能通过字节码工具库,如Javassist 。

特别是,看看AnnotationsAttribute类的例子,关于如何创build/设置字节码API的注释和教程部分,以获得如何操作类文件的一般指导。

这是简单而直接的东西 – 我不会推荐这种方法,并build议你考虑汤姆的答案,除非你需要这样做的大量的类(或所说的类是不可用的,直到运行时,从而写一个适配器是不可能的)。

关于这个话题的好文章: http : //ayoubelabbassi.blogspot.com/2011/01/how-to-add-annotations-at-runtime-to.html

也可以使用JavareflectionAPI在运行时将注释添加到Java类。 本质上,必须重新创build在类java.lang.Class (或在内部类java.lang.Class.AnnotationData定义的Java 8)中定义的内部Annotation映射。 当然,这种方法相当不方便,并且随时可能会破坏较新的Java版本。 但是,对于快速和肮脏的testing/原型,这种方法有时可能是有用的。

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); } }); } } 

用法示例:

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

输出:

 TestClass annotation before:null TestClass annotation after:@RuntimeAnnotations$TestAnnotation(value=some String) 

这种方法的局限性:

  • Java的新版本可能随时破坏代码。
  • 上面的例子仅适用于Java 8 – 使其适用于较旧的Java版本,因此需要在运行时检查Java版本并相应地更改实现。
  • 如果注释的类被重新定义 (例如在debugging过程中),则注释将会丢失。
  • 未经彻底testing; 不知道是否有任何不良的副作用 – 使用您自己的风险

可以通过代理在运行时创build注释。 然后你可以通过reflection将它们添加到你的Java对象中(正如其他答案中所build议的那样)(但是你可能会更好地寻找替代方法来处理这个问题,因为通过reflection来破坏现有types可能是危险的,而且很难debugging)。

但是这不是很容易……我写了一个叫做Javar的库,我希望能够适当的使用一个干净的API。

它在JCenter和Maven Central 。

使用它:

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

如果映射的任何条目与注释声明的字段和types不匹配,则抛出exception。 如果没有默认值的任何值丢失,则抛出exception。

这使得可以假设成功创build的每个注解实例与编译时注释实例一样安全。

作为一个奖励,这个库还可以parsing注解类,并将注解的值作为一个Map返回:

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

这对于创build迷你框架非常方便。

    Interesting Posts