Почему пропущенная аннотация не вызывает 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 есть в наличии, будет жалоба. (не уверен в этом)

Другие вопросы по тегам