Pourquoi une annotation manquante ne provoque-t-elle pas une exception ClassNotFoundException à l’exécution?

Considérez le code suivant:

A.java:

import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) @interface A{} 

C.java:

 import java.util.*; @A public class C { public static void main(Ssortingng[] args){ System.out.println(Arrays.toSsortingng(C.class.getAnnotations())); } } 

La compilation et l’exécution fonctionnent comme prévu:

 $ javac *.java $ java -cp . C [@A()] 

Mais alors considérez ceci:

 $ rm A.class $ java -cp . C [] 

Je m’attendais à ce qu’il lance une ClassNotFoundException , car @A est manquant. Mais au lieu de cela, il supprime silencieusement l’annotation.

Ce comportement est-il documenté dans le JLS quelque part ou s’agit-il d’une bizarrerie de la JVM de Sun? Quelle est la raison de cela?

Cela semble commode pour des choses comme javax.annotation.Nonnull (qui semble avoir été de @Retention(CLASS) façon @Retention(CLASS) ), mais pour de nombreuses autres annotations, il semblerait que cela puisse provoquer de mauvaises choses à l’exécution.

Dans les versions précédentes de JSR-175 (annotations), il était question de savoir si le compilateur et le moteur d’exécution devaient ignorer les annotations inconnues, afin de fournir un couplage plus souple entre l’utilisation et la déclaration des annotations. Un exemple spécifique était l’utilisation d’annotations spécifiques au serveur d’applications sur un EJB pour contrôler la configuration du déploiement. Si le même bean devait être déployé sur un serveur d’applications différent, il aurait été pratique que le moteur d’exécution ignore simplement les annotations inconnues au lieu de générer une erreur NoClassDefFoundError.

Même si la formulation est un peu vague, je suppose que le comportement que vous voyez est spécifié dans JLS 13.5.7: “… la suppression des annotations n’a aucun effet sur la liaison correcte des représentations binarys des programmes dans le langage de programmation Java . ” J’interprète cela comme si les annotations étaient supprimées (non disponibles au moment de l’exécution), le programme devrait toujours être lié et exécuté et que cela implique que les annotations inconnues sont simplement ignorées lors de l’access par reflection.

La première version du JDK 5 de Sun ne l’a pas correctement implémentée, mais elle a été corrigée au 1.5.0_06. Vous pouvez trouver le bogue pertinent 6322301 dans la firebase database de bogues, mais il ne pointe vers aucune spécification, sauf en affirmant que “selon le lead de spécification JSR-175, les annotations inconnues doivent être ignorées par getAnnotations”.

Citant le JLS:

9.6.1.2 Rétention Les annotations ne peuvent être présentes que dans le code source, ou peuvent être présentes sous la forme binary d’une classe ou d’une interface. Une annotation présente dans le fichier binary peut ou peut ne pas être disponible au moment de l’exécution via les bibliothèques de reflection de la plate-forme Java.

L’annotation du type d’annotation.Retention permet de choisir parmi les possibilités ci-dessus. Si une annotation a correspond à un type T et que T a une annotation (méta) m qui correspond à une annotation.Retention, alors:

  • Si m a un élément dont la valeur est annotation.RetentionPolicy.SOURCE, alors un compilateur Java doit s’assurer que a n’est pas présent dans la représentation binary de la classe ou de l’interface dans laquelle apparaît.
  • Si m possède un élément dont la valeur est annotation.RetentionPolicy.CLASS ou annotation.RetentionPolicy.RUNTIME, un compilateur Java doit s’assurer que a est représenté dans la représentation binary de la classe ou de l’interface dans laquelle un apparaît, sauf si m annote une déclaration de variable locale . Une annotation sur une déclaration de variable locale n’est jamais conservée dans la représentation binary.

Si T n’a pas d’annotation (méta) m qui correspond à annotation.Retention, alors un compilateur Java doit traiter T comme s’il possédait une telle méta-annotation m avec un élément dont la valeur est annotation.RetentionPolicy.CLASS.

Donc, RetentionPolicy.RUNTIME garantit que l’annotation est compilée dans le binary mais qu’une annotation présente dans le binary ne doit pas être disponible à l’exécution

Si vous avez réellement du code qui lit @A et fait quelque chose avec lui, le code a une dépendance sur la classe A, et il lancera ClassNotFoundException.

Si ce n’est pas le cas, c’est-à-dire qu’aucun code ne s’intéresse spécifiquement à @A, alors on peut soutenir que @A n’a pas vraiment d’importance.

Les annotations n’ont aucun effet direct sur le fonctionnement du code qu’elles annotent.
Cependant, en utilisant @Retention(RetentionPolicy.RUNTIME) les annotations sont disponibles au moment de l’exécution.

Maintenant, je pense que @Retention n’est pas disponible et donc ignoré. Cela implique que les autres annotations ne sont pas disponibles lors de l’exécution.
Il n’y a pas d’exception car les annotations sont ignorées par défaut. Ce n’est qu’en présence de @Retention qu’ils sont pris en compte.

Probablement si vous vous en remettez à @Retention est disponible, il y aura une plainte. (pas sûr à ce sujet)