Perché un'annotazione mancante non causa un'eccezione ClassNotFoundException in fase di esecuzione?


91

Considera il codice seguente:

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 compilazione e l'esecuzione funziona come previsto:

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

Ma poi considera questo:

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

Mi sarei aspettato che lanciasse un ClassNotFoundException, poiché @Amanca. Invece, elimina silenziosamente l'annotazione.

Questo comportamento è documentato da qualche parte nella JLS o è una stranezza della JVM di Sun? Qual è il motivo?

Sembra conveniente per cose come javax.annotation.Nonnull(che sembra che avrebbe dovuto essere @Retention(CLASS)comunque), ma per molte altre annotazioni sembra che potrebbe causare varie cose brutte in fase di esecuzione.

Risposte:


90

Nelle prime bozze pubbliche per JSR-175 (annotazioni), si discuteva se il compilatore e il runtime dovessero ignorare le annotazioni sconosciute, per fornire un accoppiamento più libero tra l'uso e la dichiarazione delle annotazioni. Un esempio specifico è stato l'uso di annotazioni specifiche del server delle applicazioni su un EJB per controllare la configurazione della distribuzione. Se lo stesso bean dovesse essere distribuito su un diverso server delle applicazioni, sarebbe stato conveniente se il runtime ignorasse semplicemente le annotazioni sconosciute invece di sollevare un'eccezione NoClassDefFoundError.

Anche se la formulazione è un po 'vaga, presumo che il comportamento che stai vedendo sia specificato in JLS 13.5.7: "... la rimozione delle annotazioni non ha effetto sul corretto collegamento delle rappresentazioni binarie dei programmi nel linguaggio di programmazione Java . " Lo interpreto come se le annotazioni venissero rimosse (non disponibile in fase di esecuzione), il programma dovrebbe comunque collegarsi ed essere eseguito e ciò implica che le annotazioni sconosciute vengono semplicemente ignorate quando si accede tramite la riflessione.

La prima versione di JDK 5 di Sun non lo implementava correttamente, ma è stato risolto nella 1.5.0_06. È possibile trovare il bug pertinente 6322301 nel database dei bug, ma non punta a nessuna specifica eccetto l'affermazione che "secondo il lead delle specifiche JSR-175, le annotazioni sconosciute devono essere ignorate da getAnnotations".


35

Citando il JLS:

9.6.1.2 Conservazione Le annotazioni possono essere presenti solo nel codice sorgente, oppure possono essere presenti nella forma binaria di una classe o interfaccia. Un'annotazione presente nel file binario può essere disponibile o meno in fase di esecuzione tramite le librerie riflettenti della piattaforma Java.

Il tipo di annotazione annotation.Retention viene utilizzato per scegliere tra le possibilità di cui sopra. Se un'annotazione a corrisponde a un tipo T e T ha una (meta-) annotazione m che corrisponde all'annotazione.

  • Se m ha un elemento il cui valore è annotation.RetentionPolicy.SOURCE, un compilatore Java deve assicurarsi che a non sia presente nella rappresentazione binaria della classe o dell'interfaccia in cui appare.
  • Se m ha un elemento il cui valore è annotation.RetentionPolicy.CLASS o annotation.RetentionPolicy.RUNTIME un compilatore Java deve assicurarsi che a sia rappresentato nella rappresentazione binaria della classe o dell'interfaccia in cui appare, a meno che m annoti una dichiarazione di variabile locale . Un'annotazione su una dichiarazione di variabile locale non viene mai mantenuta nella rappresentazione binaria.

Se T non ha una (meta-) annotazione m che corrisponde a annotation.Retention, un compilatore Java deve trattare T come se avesse una tale meta-annotazione m con un elemento il cui valore è annotation.RetentionPolicy.CLASS.

Quindi RetentionPolicy.RUNTIME garantisce che l'annotazione sia compilata nel binario ma un'annotazione presente nel binario non deve essere disponibile in fase di esecuzione


9

se hai effettivamente un codice che legge @A e fa qualcosa con esso, il codice ha una dipendenza dalla classe A e genererà ClassNotFoundException.

in caso contrario, ovvero nessun codice si preoccupa specificamente di @A, allora è discutibile che @A non abbia molta importanza.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.