简体   繁体   中英

How to work with classloader and annotations

I'm trying to get an annotation from a method but I'm having some trouble. What I'm doing is loading a jar file using URLClassLoader, then I'm inspecting the annotations on methods in classes in that jar using reflection and Method.getAnnotation.
The issue I've encountered is that Method.getAnnotation will return always return null, even if the annotation is there (which we can see using Method.getAnnotations). I've tested the same procedure on the same annotation type on classes which are not loaded with the URLClassLoader and it works fine.
More in depth investigation shows that the canonical name of both instance.annotationType() and MyAnnotation.class are identical. However instance.annotationType().equals(MyAnnotation.class) returns false. The instance itself however is a proxy (com.sun.proxy).

Is there a way to get at the annotation and the fields of the annotation without a considerable amount of reflection?

instance.annotationType().getMethod("value").invoke(instance)

The above would work to get at the field, and iterating through the result of Method.getAnnotations with string comparisons on canonical names would get me the annotation but surely there is a better way?

Also for educational value, what causes this? My guess is that it's because the class of the annotation loaded by the system class loader is somehow different than the class of the annotation loaded by the URL class loader.

A hacky solution is as follows:

Instead of AccessibleObject.getAnnotation(Class annotationClass) use

private static Annotation getAnnotation(AccessibleObject object, Class annotationClass){
    for(Annotation a : object.getAnnotations()){
        if(a.annotationType().getCanonicalName().equals(annotationClass.getCanonicalName()))
            return a;
    }
    return null;
}

Instead of MyAnnotation.value() use

private static Object getAnnotationFieldWithReflection(Annotation annotation, String fieldName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    return annotation.annotationType().getMethod(fieldName).invoke(annotation);
}

I came across the same issue and it was quite hard to debug. My solution to this was to use the same classloader from a class instance reference.

For example, given a Jar file you want to load the class found there, you can reference a class instance from the package and get the classloader it uses:

URLClassLoader
    .newInstance(arrayOf(URL("jar:file:${it.path}!/")), AGivenClass::class.java.classLoader)

I then could successful collect the annotations of interest.

This is an example in Kotlin, as the same issue happened there, so I hope it can be of any help for future readers.

I think that ClassLoader#loadClass(name, true) is needed, which will actually load the class fully (resolve=true) . Note that this also initializes the class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM