web-dev-qa-db-fra.com

Pourquoi une annotation manquante ne provoque-t-elle pas une exception ClassNotFoundException au moment de 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(String[] args){
                System.out.println(Arrays.toString(C.class.getAnnotations()));
        }
}

Compiler et exécuter les travaux 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 un ClassNotFoundException, car @A Est manquant. Mais à la place, il supprime silencieusement l'annotation.

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

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

86
Matt McHenry

Dans les versions préliminaires publiques de JSR-175 (annotations), il a été discuté si le compilateur et le runtime 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 autre serveur d'applications, cela aurait été pratique si le runtime ignorait 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 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 d'un accès via la réflexion.

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

86
jarnbjo

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. ne annotation qui est présente dans le binaire peut être disponible ou non au moment de l'exécution via les bibliothèques réfléchissantes de la plate-forme Java.

Le type d'annotation annotation.Retention est utilisé pour 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. Rétention, 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 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 a 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 d'annotation (méta-) 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 valeur est annotation.RetentionPolicy.CLASS.

Ainsi, RetentionPolicy.RUNTIME garantit que l'annotation est compilée dans le binaire mais une annotation présente dans le binaire n'a pas à être disponible au moment de l'exécution

32
Guillaume

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 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.

7
irreputable