Est-il possible d’append une annotation à un object (dans mon cas en particulier, une méthode) à l’exécution?
Pour un peu plus d’explication: j’ai deux modules, moduleA et moduleB. moduleB dépend du moduleA, qui ne dépend de rien. (modA est mon type de base et interfaces de base et tel, modB est db / couche de données) modB dépend également de la bibliothèque externe. Dans mon cas, modB transmet une classe de modA à externalLibrary, ce qui nécessite l’annotation de certaines méthodes. Les annotations spécifiques font toutes partie de externalLib et, comme je l’ai dit, modA ne dépend pas de externalLib et j’aimerais que cela rest ainsi.
Alors, est-ce possible, ou avez-vous des suggestions pour d’autres façons de voir ce problème?
Il n’est pas possible d’append une annotation à l’exécution, il semble que vous ayez besoin d’introduire un adaptateur que le module B utilise pour envelopper l’object du module A en exposant les méthodes annotées requirejses.
C’est possible via la bibliothèque d’instruments bytecode telle que Javassist .
En particulier, consultez la classe AnnotationsAtsortingbute pour un exemple de création / définition d’annotations et de section de didacticiel sur l’API bytecode pour des instructions générales sur la manipulation des fichiers de classe.
Ceci est tout sauf simple – je ne recommanderais PAS cette approche et suggérerais plutôt de prendre en compte la réponse de Tom, à moins que vous ayez besoin de le faire pour un grand nombre de classes (ou que ces classes un adaptateur est impossible).
Il est également possible d’append une annotation à une classe Java lors de l’exécution à l’aide de l’API de reflection Java. Essentiellement, il faut recréer les cartes d’annotation internes définies dans la classe java.lang.Class
(ou pour Java 8 définie dans la classe interne java.lang.Class.AnnotationData
). Naturellement, cette approche est plutôt piratée et pourrait se briser à tout moment pour les nouvelles versions de Java. Mais pour des tests / prototypes rapides et sales, cette approche peut parfois être utile.
Exemple de démonstration de concept pour 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 void putAnnotation(Class> c, Class annotationClass, Map valuesMap){ putAnnotation(c, annotationClass, annotationForMap(annotationClass, valuesMap)); } public static void putAnnotation(Class> c, Class 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 Object /*AnnotationData*/ createAnnotationData(Class> c, Object /*AnnotationData*/ annotationData, Class annotationClass, T annotation, int classRedefinedCount) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Map, Annotation> annotations = (Map, Annotation>) AnnotationData_annotations.get(annotationData); Map, Annotation> declaredAnnotations= (Map, Annotation>) AnnotationData_declaredAnotations.get(annotationData); Map, Annotation> newDeclaredAnnotations = new LinkedHashMap<>(annotations); newDeclaredAnnotations.put(annotationClass, annotation); Map, 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 annotationForMap(final Class annotationClass, final Map valuesMap){ return (T)AccessController.doPrivileged(new PrivilegedAction(){ 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); } }); } }
Exemple d’utilisation:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface TestAnnotation { Ssortingng value(); } public static class TestClass{} public static void main(Ssortingng[] args) { TestAnnotation annotation = TestClass.class.getAnnotation(TestAnnotation.class); System.out.println("TestClass annotation before:" + annotation); Map 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); }
Sortie:
TestClass annotation before:null TestClass annotation after:@RuntimeAnnotations$TestAnnotation(value=some Ssortingng)
Limites de cette approche:
Il est possible de créer des annotations à l’exécution via un proxy . Vous pouvez ensuite les append à vos objects Java par reflection, comme suggéré dans d’autres réponses (mais vous feriez probablement mieux de trouver une autre solution, car manipuler les types existants par reflection peut être dangereux et difficile à déboguer).
Mais ce n’est pas très facile … J’ai écrit une bibliothèque appelée, j’espère, Javanna juste pour le faire facilement en utilisant une API propre.
C’est dans JCenter et Maven Central .
En l’utilisant:
@Retention( RetentionPolicy.RUNTIME ) @interface Simple { Ssortingng value(); } Simple simple = Javanna.createAnnotation( Simple.class, new HashMap() {{ put( "value", "the-simple-one" ); }} );
Si une entrée de la carte ne correspond pas à la ou aux zone (s) et type (s) déclarés, une exception est émise. Si une valeur sans valeur par défaut est manquante, une exception est levée.
Cela permet de supposer que chaque instance d’annotation créée avec succès est aussi sûre qu’une instance d’annotation de compilation.
En prime, cette lib peut également parsingr les classes d’annotation et renvoyer les valeurs de l’annotation en tant que carte:
Map values = Javanna.getAnnotationValues( annotation );
Ceci est pratique pour créer des mini-frameworks.