Почему отсутствующая аннотация не вызывает ClassNotFoundException во время выполнения? - PullRequest
80 голосов
/ 25 августа 2010

Рассмотрим следующий код:

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 где-то или это причуды JVM от Sun?В чем смысл?

Кажется, это удобно для таких вещей, как javax.annotation.Nonnull (кажется, это должно было быть @Retention(CLASS) в любом случае), но для многих других аннотаций кажется, что это может вызвать различные плохие вещипроизойти во время выполнения.

Ответы [ 3 ]

83 голосов
/ 25 августа 2010

В более ранних открытых проектах JSR-175 (аннотации) обсуждалось, должен ли компилятор и среда выполнения игнорировать неизвестные аннотации, чтобы обеспечить более слабую связь между использованием и объявлением аннотаций. Конкретным примером было использование специфичных для сервера приложений аннотаций в EJB для управления конфигурацией развертывания. Если один и тот же компонент должен быть развернут на другом сервере приложений, было бы удобно, если бы среда выполнения просто игнорировала неизвестные аннотации вместо вызова NoClassDefFoundError.

Даже если формулировка немного расплывчата, я предполагаю, что поведение, которое вы видите, указано в JLS 13.5.7: «... удаление аннотаций не влияет на правильную связь двоичных представлений программ в Язык программирования Java. " Я интерпретирую это так, как будто аннотации удаляются (недоступно во время выполнения), программа все равно должна связываться и запускаться, и это означает, что неизвестные аннотации просто игнорируются при доступе через отражение.

Первый выпуск Sun JDK 5 не реализовал это правильно, но это было исправлено в 1.5.0_06. Вы можете найти соответствующую ошибку 6322301 в базе данных ошибок, но она не указывает на какие-либо спецификации, за исключением заявления о том, что "в соответствии со спецификацией JSR-175 неизвестные аннотации должны игнорироваться getAnnotations".

32 голосов
/ 25 августа 2010

Цитирование JLS:

9.6.1.2 Сохранение Аннотации могут присутствовать только в исходном коде или в двоичном виде класса или интерфейса, Аннотация, присутствующая в двоичном файле, может или не может быть доступна во время выполнения через отражающие библиотеки платформы Java.

Тип аннотации annotation.Retention используется для выбора междувышеуказанные возможности.Если аннотация a соответствует типу T, а T имеет (мета-) аннотацию m, соответствующую annotation.Retention, то:

  • Если m имеет элемент, значение которого равно annotation.RetentionPolicy.ИСТОЧНИК, тогда компилятор 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 гарантирует, что аннотация компилируется в двоичный файл, но аннотация, присутствующая в двоичном файле, не должна быть доступна во время выполнения

7 голосов
/ 25 августа 2010

если у вас действительно есть код, который читает @A и что-то с ним делает, код имеет зависимость от класса A. Он выдает исключение ClassNotFoundException.

, если нет, то есть ни один код не заботится конкретно о @A, тогда можно утверждать, что @A на самом деле не имеет значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...