Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the defining classloader of a class level annotation always a parent of the initiating classloader of that class?

Suppose the following:

@SomeAnnotation
public interface Foo {
}

I would like to know if it is always the case that either the defining classloader of SomeAnnotation is equal to or a parent of the initiating classloader of Foo.

I have read JVMS v8 section 5.3. but I'm not sure what applies here. Section 5.3.4 talks about loading constraints, but they seem not to apply for annotations.

The question I'm asking is because code like this:

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

will fail in the presence of different classloaders. I know I could use getAnnotations and search in the resulting array for an element whose class name is equal to the name of SomeAnnotation. But I'm wondering if the following will work too:

    Class<?> fooClass = //will in some way obtain a reference to class Foo
    fooClass.getAnnotation((Class<? extends Annotation>) fooClass
            .getClassLoader().loadClass(SomeAnnotation.class.getName()));
like image 676
SpaceTrucker Avatar asked May 02 '14 21:05

SpaceTrucker


1 Answers

The short answer: no

The long answer.

RetentionPolicy.RUNTIME annotations are available for discovery via the reflection API only. This is done to ensure loose coupling between annotations and annotated code. According to this bug report, getAnnotations() must skip unknown annotations which implies that it's ok to have annotations that are not recognized by the classloader. The behavior of real Java code discussed here validates that assumption.

This behavior has two implications:

  1. All unrecognized annotations (e.g. the ones not in classpath) become "invisible"
  2. In order to reveal them, the class must be completely reloaded by a different classloader that has access to both, the type and annotations.

For example if somepkg.SomeAnnotation was not in classpath when someClass was loaded, this will not work:

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

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

But this will:

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

ClassLoader cl = new URLClassLoader(classPathWithSomeClassAndAnnotations, null);
Annotation a = cl.loadClass(someClass.getName()).getAnnotation(cl.loadClass("somepkg.SomeAnnotation"));
like image 169
anttix Avatar answered Oct 06 '22 01:10

anttix