Почему пропущенная аннотация не вызывает ClassNotFoundException во время выполнения?
Рассмотрим следующий код:
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()));
}
}
Компиляция и запуск работ, как и ожидалось:
$ javac *.java
$ java -cp . C
[@A()]
Но тогда подумайте об этом:
$ rm A.class
$ java -cp . C
[]
Я бы ожидал, что это бросить ClassNotFoundException
, поскольку @A
пропал, отсутствует. Но вместо этого он молча отбрасывает аннотацию.
Это где-то задокументировано в JLS или это причуды Sun JVM? В чем причина этого?
Это кажется удобным для таких вещей, как javax.annotation.Nonnull
(что должно было быть @Retention(CLASS)
в любом случае), но для многих других аннотаций кажется, что это может вызвать различные плохие вещи во время выполнения.
3 ответа
В более ранних открытых проектах JSR-175 (аннотации) обсуждалось, должен ли компилятор и среда выполнения игнорировать неизвестные аннотации, чтобы обеспечить более слабую связь между использованием и объявлением аннотаций. Конкретным примером было использование специфичных для сервера приложений аннотаций в EJB для управления конфигурацией развертывания. Если один и тот же компонент должен быть развернут на другом сервере приложений, было бы удобно, если бы среда выполнения просто игнорировала неизвестные аннотации вместо вызова NoClassDefFoundError.
Даже если формулировка немного расплывчата, я предполагаю, что поведение, которое вы видите, указано в JLS 13.5.7: "... удаление аннотаций не влияет на правильную связь двоичных представлений программ в языке программирования Java ". Я интерпретирую это так, как если бы аннотации были удалены (недоступны во время выполнения), программа все еще должна связываться и запускаться, и это означает, что неизвестные аннотации просто игнорируются при доступе через отражение.
Первый выпуск Sun JDK 5 не реализовал это правильно, но это было исправлено в 1.5.0_06. Вы можете найти соответствующую ошибку 6322301 в базе данных ошибок, но она не указывает на какие-либо спецификации, кроме заявления о том, что "в соответствии со спецификацией JSR-175 неизвестные аннотации должны игнорироваться getAnnotations".
Цитируя JLS:
9.6.1.2 Хранение Аннотации могут присутствовать только в исходном коде или могут присутствовать в двоичной форме класса или интерфейса. Аннотация, которая присутствует в двоичном файле, может или не может быть доступна во время выполнения через отражающие библиотеки платформы Java.
Тип аннотации annotation.Retention используется для выбора из перечисленных выше возможностей. Если аннотация a соответствует типу T, а T имеет (мета) аннотацию m, которая соответствует annotation.Retention, то:
- Если m имеет элемент, значение которого равно annotation.RetentionPolicy.SOURCE, то компилятор Java должен убедиться, что a отсутствует в двоичном представлении класса или интерфейса, в котором появляется a.
- Если у m есть элемент, значение которого равно annotation.RetentionPolicy.CLASS или annotation.RetentionPolicy.RUNTIME, компилятор Java должен убедиться, что a представлено в двоичном представлении класса или интерфейса, в котором появляется a, если только m не аннотирует объявление локальной переменной, Аннотация к объявлению локальной переменной никогда не сохраняется в двоичном представлении.
Если T не имеет (мета) аннотации m, которая соответствует annotation.Retention, то компилятор Java должен обрабатывать T так, как если бы он имел такую метааннотацию m с элементом, значение которого равно annotation.RetentionPolicy.CLASS.
Таким образом, RetentionPolicy.RUNTIME гарантирует, что аннотация скомпилирована в двоичный файл, но аннотация, присутствующая в двоичном файле, не должна быть доступна во время выполнения
Если на самом деле у вас есть код, который читает @A и что-то с ним делает, он будет зависеть от класса A и вызовет исключение ClassNotFoundException.
если нет, то есть никакой код не заботится конкретно о @A, то можно утверждать, что @A на самом деле не имеет значения.
Аннотации не оказывают прямого влияния на работу кода, который они аннотируют.
Однако с помощью @Retention(RetentionPolicy.RUNTIME)
аннотации становятся доступными во время выполнения.
Теперь я думаю, что @Retention
недоступен и поэтому игнорируется. Это подразумевает, что другие аннотации не доступны во время выполнения.
Исключений нет, поскольку аннотации по умолчанию игнорируются. Только при наличии @Retention
они считаются.
Вероятно, если вы делаете Shure @Retention
есть в наличии, будет жалоба. (не уверен в этом)