Warum verursacht eine fehlende Annotation zur Laufzeit keine ClassNotFoundException?

91

Betrachten Sie den folgenden Code:

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

Das Kompilieren und Ausführen funktioniert wie erwartet:

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

Aber dann bedenken Sie Folgendes:

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

Ich hätte erwartet, dass es einen wirft ClassNotFoundException, da @Aes fehlt. Stattdessen wird die Anmerkung stillschweigend gelöscht.

Ist dieses Verhalten irgendwo in der JLS dokumentiert oder ist es eine Eigenart von Suns JVM? Was ist der Grund dafür?

Es scheint praktisch für Dinge wie javax.annotation.Nonnull(was @Retention(CLASS)sowieso hätte sein sollen), aber für viele andere Anmerkungen scheint es, dass es zur Laufzeit verschiedene schlechte Dinge verursachen könnte.

Matt McHenry
quelle

Antworten:

90

In den früheren öffentlichen Entwürfen für JSR-175 (Annotationen) wurde diskutiert, ob der Compiler und die Laufzeit unbekannte Annotationen ignorieren sollten, um eine lockere Kopplung zwischen der Verwendung und der Deklaration von Annotationen zu gewährleisten. Ein spezielles Beispiel war die Verwendung von anwendungsserverspezifischen Anmerkungen auf einer EJB zur Steuerung der Bereitstellungskonfiguration. Wenn dieselbe Bean auf einem anderen Anwendungsserver bereitgestellt werden sollte, wäre es praktisch gewesen, wenn die Laufzeit die unbekannten Anmerkungen einfach ignoriert hätte, anstatt einen NoClassDefFoundError auszulösen.

Auch wenn der Wortlaut etwas vage ist, gehe ich davon aus, dass das angezeigte Verhalten in JLS 13.5.7 angegeben ist: "... das Entfernen von Anmerkungen hat keinen Einfluss auf die korrekte Verknüpfung der binären Darstellungen von Programmen in der Programmiersprache Java . " Ich interpretiere dies so, als ob Annotationen entfernt würden (zur Laufzeit nicht verfügbar), das Programm weiterhin verknüpft und ausgeführt werden sollte und dies impliziert, dass die unbekannten Annotationen beim Zugriff durch Reflektion einfach ignoriert werden.

Die erste Version von Suns JDK 5 hat dies nicht korrekt implementiert, wurde jedoch in 1.5.0_06 behoben. Sie können den relevanten Fehler 6322301 in der Fehlerdatenbank finden , aber er verweist nicht auf Spezifikationen, außer der Behauptung, dass "gemäß dem JSR-175-Spezifikationshinweis unbekannte Anmerkungen von getAnnotations ignoriert werden müssen".

jarnbjo
quelle
35

Zitieren der JLS:

9.6.1.2 Aufbewahrungsanmerkungen dürfen nur im Quellcode oder in binärer Form einer Klasse oder Schnittstelle vorhanden sein. Eine in der Binärdatei vorhandene Anmerkung kann zur Laufzeit über die reflektierenden Bibliotheken der Java-Plattform verfügbar sein oder nicht.

Der Annotationstyp Annotation.Retention wird verwendet, um zwischen den oben genannten Möglichkeiten zu wählen. Wenn eine Annotation a einem Typ T entspricht und T eine (Meta-) Annotation m hat, die einer Annotation entspricht.

  • Wenn m ein Element hat, dessen Wert annotation.RetentionPolicy.SOURCE ist, muss ein Java-Compiler sicherstellen, dass a in der binären Darstellung der Klasse oder Schnittstelle, in der a erscheint, nicht vorhanden ist.
  • Wenn m ein Element hat, dessen Wert annotation.RetentionPolicy.CLASS oder annotation.RetentionPolicy.RUNTIME ist, muss ein Java-Compiler sicherstellen, dass a in der binären Darstellung der Klasse oder Schnittstelle dargestellt wird, in der a erscheint, es sei denn, m kommentiert eine lokale Variablendeklaration . Eine Anmerkung zu einer lokalen Variablendeklaration wird in der Binärdarstellung niemals beibehalten.

Wenn T keine (Meta-) Annotation m hat, die annotation.Retention entspricht, muss ein Java-Compiler T so behandeln, als hätte er eine solche Meta-Annotation m mit einem Element, dessen Wert annotation.RetentionPolicy.CLASS ist.

RetentionPolicy.RUNTIME stellt also sicher, dass die Annotation in die Binärdatei kompiliert wird, aber eine in der Binärdatei vorhandene Annotation muss zur Laufzeit nicht verfügbar sein

Guillaume
quelle
9

Wenn Sie tatsächlich Code haben, der @A liest und etwas damit macht, ist der Code von Klasse A abhängig und löst ClassNotFoundException aus.

Wenn nicht, dh kein Code kümmert sich speziell um @A, dann ist es wahrscheinlich, dass @A nicht wirklich wichtig ist.

unwiderlegbar
quelle