Pourquoi une annotation manquante ne provoque-t-elle pas une exception ClassNotFoundException au moment de l'exécution?

91

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(String[] args){
                System.out.println(Arrays.toString(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 me serais attendu à ce qu'il lance un ClassNotFoundException, car il @Amanque. Mais au lieu de cela, il supprime silencieusement l'annotation.

Ce comportement est-il documenté quelque part dans le JLS ou est-ce une bizarrerie de la JVM de Sun? Quelle en est la raison?

Cela semble pratique pour des choses comme javax.annotation.Nonnull(ce qui semble avoir dû l'être de @Retention(CLASS)toute façon), mais pour de nombreuses autres annotations, il semble que cela puisse provoquer diverses mauvaises choses au moment de l'exécution.

Matt McHenry
la source

Réponses:

90

Dans les brouillons publics précédents pour JSR-175 (annotations), il a été discuté si le compilateur et le moteur d'exécution devaient ignorer les annotations inconnues, pour fournir un couplage plus lâche 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 de déploiement. Si le même bean devait être déployé sur un serveur d'application différent, il aurait été pratique que le runtime ignore simplement les annotations inconnues au lieu de déclencher une NoClassDefFoundError.

Même si le libellé 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 binaires 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 cela implique que les annotations inconnues sont simplement ignorées lors de l'accès par réflexion.

La première version du JDK 5 de Sun ne l'implémentait pas correctement, mais elle a été corrigée dans la 1.5.0_06. Vous pouvez trouver le bogue correspondant 6322301 dans la base de données des bogues, mais il ne pointe vers aucune spécification, sauf en affirmant que "selon la spécification JSR-175, les annotations inconnues doivent être ignorées par getAnnotations".

Jarnbjo
la source
35

Citant le JLS:

9.6.1.2 Rétention Les annotations peuvent être présentes uniquement dans le code source, ou elles peuvent être présentes sous la forme binaire d'une classe ou d'une interface. Une annotation présente dans le binaire peut ou non être disponible au moment de l'exécution via les bibliothèques réfléchissantes de la plate-forme Java.

L'annotation du type d'annotation La rétention permet de choisir parmi les possibilités ci-dessus. Si une annotation a correspond à un type T, et T a une (méta) annotation m qui correspond à une annotation.

  • 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 binaire de la classe ou de l'interface dans laquelle a apparaît.
  • Si m a 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 binaire de la classe ou de l'interface dans laquelle a 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 binaire.

Si T n'a pas de (méta) annotation m qui correspond à annotation.Retention, alors un compilateur Java doit traiter T comme s'il avait 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 binaire, mais une annotation présente dans le binaire n'a pas besoin d'être disponible au moment de l'exécution

Guillaume
la source
9

si vous avez en fait du code qui lit @A et en fait quelque chose, le code a une dépendance sur la classe A, et il lèvera ClassNotFoundException.

sinon, c'est-à-dire qu'aucun code ne se soucie spécifiquement de @A, alors on peut soutenir que @A n'a pas vraiment d'importance.

irréprochable
la source