简体   繁体   English

如何使用类加载器和注释

[英]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.我正在做的是使用 URLClassLoader 加载一个 jar 文件,然后我使用反射和 Method.getAnnotation 检查该 jar 中类中方法的注释。
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).我遇到的问题是 Method.getAnnotation 将始终返回 null,即使注释存在(我们可以使用 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.我已经在未使用 URLClassLoader 加载的类上的相同注释类型上测试了相同的过程,并且工作正常。
More in depth investigation shows that the canonical name of both instance.annotationType() and MyAnnotation.class are identical.更深入的调查显示 instance.annotationType() 和 MyAnnotation.class 的规范名称是相同的。 However instance.annotationType().equals(MyAnnotation.class) returns false.然而 instance.annotationType().equals(MyAnnotation.class) 返回 false。 The instance itself however is a proxy (com.sun.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?上面的方法可以到达该字段,并且通过对规范名称进行字符串比较来迭代 Method.getAnnotations 的结果可以获得注释,但肯定有更好的方法吗?

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.我的猜测是,这是因为系统 class 加载程序加载的注释的 class 与 URL class 加载程序加载的注释的 class 在某种程度上不同。

A hacky solution is as follows:一个骇人听闻的解决方案如下:

Instead of AccessibleObject.getAnnotation(Class annotationClass) use而不是 AccessibleObject.getAnnotation(Class annotationClass) 使用

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而不是 MyAnnotation.value() 使用

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.我对此的解决方案是使用来自 class 实例引用的相同类加载器。

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:例如,给定一个 Jar 文件,您想要加载在那里找到的 class,您可以从 package 引用一个 class 实例并获取它使用的类加载器:

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.这是Kotlin中的一个例子,因为那里也发生过同样的问题,所以希望对以后的读者有所帮助。

I think that ClassLoader#loadClass(name, true) is needed, which will actually load the class fully (resolve=true) .我认为需要ClassLoader#loadClass(name, true) ,它实际上会完全加载 class (resolve=true) Note that this also initializes the class.请注意,这还会初始化 class。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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