Является ли определяющий загрузчик классов аннотацией уровня класса всегда родителем инициализируемого загрузчика классов этого класса?

Предположим следующее:

@SomeAnnotation
public interface Foo {
}

Я хотел бы знать, всегда ли так, что либо определяющий загрузчик классов SomeAnnotation равен, либо родитель инициализируемого загрузчика классов Foo.

Я прочитал JVMS v8 раздел 5.3. но я не уверен, что здесь применяется. В разделе 5.3.4 описаны ограничения загрузки, но они, похоже, не применяются для аннотаций.

Вопрос, который я задаю, - это код, подобный этому:

    Class<?> fooClass = //will in some way obtain a reference to class Foo
    fooClass.getAnnotation(SomeAnnotation.class);

выйдет из строя при наличии разных загрузчиков классов. Я знаю, что я мог бы использовать getAnnotations и искать в результирующем массиве для элемента, имя класса которого равно имени SomeAnnotation. Но мне интересно, будет ли работать следующее:

    Class<?> fooClass = //will in some way obtain a reference to class Foo
    fooClass.getAnnotation((Class<? extends Annotation>) fooClass
            .getClassLoader().loadClass(SomeAnnotation.class.getName()));

Ответ 1

Короткий ответ: no

Длинный ответ.

RetentionPolicy.RUNTIME аннотации доступны для обнаружения только через API отражения. Это делается для обеспечения свободной связи между аннотациями и аннотированным кодом. Согласно этот отчет об ошибках, getAnnotations() должен пропустить неизвестные аннотации, что означает, что в нем есть аннотации, которые не распознаются загрузчиком классов. Поведение реального Java-кода, обсуждаемое здесь, подтверждает это предположение.

Это поведение имеет два значения:

  • Все непризнанные аннотации (например, те, что не в classpath) становятся "невидимыми"
  • Чтобы выявить их, класс должен быть полностью перезагружен другим загрузчиком классов, который имеет доступ к обоим типам и аннотациям.

Например, если somepkg.SomeAnnotation не был в пути к классам при загрузке someClass, это не сработает:

Class<?> someClass = ....
URL [] classPathWithAnnotations = ....

ClassLoader cl = new URLClassLoader(classPathWithAnnotations);
Annotation a = someClass.getAnnotation(cl.loadClass("somepkg.SomeAnnotation"));
// a will be null

Но это будет:

Class<?> someClass = ....
URL [] classPathWithSomeClassAndAnnotations = ....

ClassLoader cl = new URLClassLoader(classPathWithSomeClassAndAnnotations, null);
Annotation a = cl.loadClass(someClass.getName()).getAnnotation(cl.loadClass("somepkg.SomeAnnotation"));