suppresswarnings son sirven sirve que para las introspección ejemplos ejemplo crear anotaciones annotation java annotations runtime

java - son - para que sirven las anotaciones



Adición de anotaciones de Java en tiempo de ejecución (4)

Es posible a través de la biblioteca de instrumentación de Javassist como Javassist .

En particular, eche un vistazo a la clase AnnotationsAttribute para ver un ejemplo sobre cómo crear / configurar anotaciones y la sección de tutoriales en la API de bytecode para obtener pautas generales sobre cómo manipular los archivos de clase.

Sin embargo, esto es todo menos simple y directo: NO recomendaría este enfoque y le sugeriría que considere la respuesta de Tom a menos que necesite hacer esto para una gran cantidad de clases (o dichas clases no están disponibles para usted hasta el tiempo de ejecución y, por lo tanto, escriben un adaptador es imposible).

¿Es posible agregar una anotación a un objeto (en mi caso en particular, un Método) en tiempo de ejecución?

Para una explicación un poco más: tengo dos módulos, moduleA y moduleB. El módulo B depende del módulo A, que no depende de nada. (modA son mis tipos de datos e interfaces principales y, por lo tanto, modB es db / capa de datos) modB también depende de la biblioteca externa. En mi caso, modB está transfiriendo una clase de modA a externalLibrary, que necesita ciertos métodos para ser anotados. Las anotaciones específicas son parte de externalLib y, como dije, modA no depende de externalLib y me gustaría que siga siendo así.

Entonces, ¿es esto posible, o tiene sugerencias para otras formas de ver este problema?


Es posible crear anotaciones en tiempo de ejecución a través de un Proxy . Luego puede agregarlos a sus objetos Java a través de la reflexión como se sugiere en otras respuestas (pero probablemente sería mejor encontrar una forma alternativa de manejar eso, ya que entrometerse con los tipos existentes a través de la reflexión puede ser peligroso y difícil de depurar).

Pero no es muy fácil ... escribí una biblioteca llamada, espero apropiadamente, Javanna solo para hacer esto fácilmente usando una API limpia.

Está en JCenter y Maven Central .

Usándolo:

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

Si alguna entrada del mapa no coincide con los campos y tipos de anotación declarados, se genera una excepción. Si falta algún valor que no tenga un valor predeterminado, se lanza una excepción.

Esto permite asumir que cada instancia de anotación que se crea con éxito es tan segura de usar como una instancia de anotación en tiempo de compilación.

Como beneficio adicional, esta biblioteca también puede analizar las clases de anotación y devolver los valores de la anotación como un Mapa:

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

Esto es conveniente para crear mini-marcos.


No es posible agregar una anotación en tiempo de ejecución, parece que necesita introducir un adapter que el módulo B utiliza para envolver el objeto del módulo A exponiendo los métodos anotados requeridos.


También es posible agregar una anotación a una clase de Java en tiempo de ejecución utilizando la API de reflexión de Java. Esencialmente, uno debe recrear los mapas de anotación internos definidos en la clase java.lang.Class (o para Java 8 definido en la clase interna java.lang.Class.AnnotationData ). Naturalmente, este enfoque es bastante intrincado y podría romperse en cualquier momento para las nuevas versiones de Java. Pero para pruebas / prototipos rápidos y sucios, este enfoque puede ser útil a veces.

Proove of concept example para 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); } }); } }

Ejemplo de uso:

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

Salida:

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

Limitaciones de este enfoque:

  • Las nuevas versiones de Java pueden romper el código en cualquier momento.
  • El ejemplo anterior solo funciona para Java 8; hacer que funcione para versiones anteriores de Java requeriría verificar la versión de Java en tiempo de ejecución y cambiar la implementación en consecuencia.
  • Si la Clase anotada se redefined (por ejemplo, durante la depuración), la anotación se perderá.
  • No probado a fondo; No estoy seguro de si hay efectos secundarios negativos: úselo bajo su propio riesgo ...