反映返回包含空元素的集合

3

我正在使用Reflections来查找具有特定注释的类。 我的项目结构如下:

一个WAR包: WEB-INF/classes/...packages.../ClassAnnoted1.class

一个JAR包被WAR包包含,其中包含一个执行此代码的类:

Reflections reflections= new Reflections(ClasspathHelper.forWebInfClasses(servletContext))
Set set= reflections.getTypesAnnotatedWith(CustomAnnotation.class)

CustomAnnotation也存在于JAR包中。

集合大小是正确的(即如果我在WAR和JAR中有3个带注释的类,则集合大小将返回为3),但其中所有元素都为null,而不是Class。我需要获取类并检查JAR中类的注释参数。

有人知道为什么会发生这种情况吗?

编辑:

Reflections reflections= new Reflections("com.my.customAnnotededClasses"); //package that my annoted class is in
Set set= reflections.getTypesAnnotatedWith(CustomAnnotation.class);

在这种情况下也不起作用,此时设置长度为零,而不是具有注释的类数。
编辑2: 好的,真正的问题是我把整个应用程序打包成了EAR文件,所以我有以下内容:
EAR
----> WAR
----> JAR

该 Jar 包已包含在 EAR 应用的 lib 文件夹中,而不是 WAR 应用的 lib 文件夹中。因此,Jar 包类无法看到 WAR 应用程序的类。一旦我像这样直接使 WAR 应用程序依赖于 Jar 包:
EAR
----> WAR
---------> JAR

它开始工作了。但是原来的问题仍然存在,可能会出现这样的情况:我想将Jar类包括在EAR中,而不是WAR中(例如,如果我有多个需要使用我的jar的war)。


你的注解类上标记了@Retention(RetentionPolicy.RUNTIME)吗? - Display Name is missing
@better_use_mkstemp 是的 - Hoffmann
多重映射 'reflections.getStore().get(TypeAnnotationsScanner.class)' 是否包含您想要的内容?您是否正在使用 OSGi 容器? - zapp
4个回答

2

我猜 reflections 库无法完成这项任务。所以我手动完成了它:

public static List<Class<?>> getClassesAnnotatedWith(Class annotation, ServletContext servletContext) {
    List<Class<?>> webClasses, jarClasses;
    webClasses= getClassesAnnotedWithFromClassLoader(annotation, servletContext.getClassLoader());
    jarClasses= getClassesAnnotedWithFromClassLoader(annotation, Thread.currentThread().getContextClassLoader());
    for (Class<?> jarClass : jarClasses) {
        Class<?> elementToAdd= null;
        for (Class<?> webClass : webClasses) {
            if ( ! jarClass.getName().equals(webClass.getName())) {
                elementToAdd= jarClass;
            }
        }
        if(elementToAdd != null) {
            webClasses.add(elementToAdd);
        }
    }
    return webClasses;
}


 private static List<Class<?>> getClassesAnnotedWithFromClassLoader(Class annotation, ClassLoader classLoader) {
        List<Class<?>> classes= new ArrayList<Class<?>>();
        Class<?> classLoaderClass= classLoader.getClass();
        while (! classLoaderClass.getName().equals("java.lang.ClassLoader")) {
            classLoaderClass= classLoaderClass.getSuperclass();
        }
        try {
            Field fldClasses= classLoaderClass.getDeclaredField("classes");
            fldClasses.setAccessible(true);
            Vector<Class<?>> classesVector= (Vector<Class<?>>) fldClasses.get(classLoader);
            for (Class c : classesVector) {
                if (c.isAnnotationPresent(annotation)) {
                    classes.add(c);
                }
            }
        } catch (Exception e) { }
        return classes;
    }

我通过ServletContext对象从我的WAR包中获取ClassLoader。如果一个类在WAR和JAR中都有相同的注释和名称(你应该检查包是否相同),则还有一种保护措施。
请注意,您可能永远不应该在自己的项目中使用此代码(也许只用于调试)。这涉及反射ClassLoader类以使“classes”属性公开。例如,在Java 9中可能不存在此属性,因此请注意。如果您正在与第三方编写的模块交互,则这也可能存在某些安全问题。

喜欢你的回答 - 它充分利用了反射 API 的功能,并且可以在未来被重复使用或修改。但这是 wrm 评论中描述的解决方案,所以我希望你给予 wrm 而不是自己奖励。干杯 :^) - Glen Best
"classLoaderClass.getDeclaredField("classes")"有问题。 - zapp
@zapp 我知道这远非是一个好的解决方案,但是为什么这样做是一个坏主意呢? - Hoffmann

1
我曾经遇到过类似的问题。你确定已经将注释类包含在你的类路径中了吗?如果它们没有被加载,它们会以某种方式被找到但实际上不会返回任何异常或其他东西。

对我来说,这仍然听起来像是类路径问题...你尝试过剪切“反射”,使用直接的Java反射列出类上的注释吗?也可能是不同的类加载器加载了注释类。即.war和.jar使用了不同的类加载器。 - wrm
我怀疑类加载器是问题所在,但我对Java中的类加载器和反射并不是很有经验,所以我不能完全确定。如果不使用反射库,我该如何解决这个问题? - Hoffmann
你知道如何从JAR类中获取WAR类加载器吗?我正在将我的应用程序打包为一个包含WAR和JAR的EAR。 - Hoffmann
问题是我不知道 WEB 项目上会有哪些类,可能没有,也可能有几十个类都带有我的 CustomAnnotation。 - Hoffmann
我找到的唯一一种从WAR项目加载类的方法是获取它的ClassLoader(使用ServletContext对象),并使用该ClassLoader加载它,JAR ClassLoader无法看到WAR类。 - Hoffmann
显示剩余4条评论

0

Reflections库给我带来了各种问题。现在我正在使用Guava库的反射部分:到目前为止,没有发生任何意外行为。 无论如何,我认为问题的根源很少是Java类加载器。 也许在使用Reflections API之前尝试加载CustomAnnotation.class类。


0

你的代码应该在传统环境中运行。

然而,在不同的环境下,例如 osgi,你会遇到以下问题:

1)使用不同协议的 URL(bundle/vfs/...)

2)不同的类加载器。

对于第一种情况,你应该a)添加相关的 UrlType(请参考Vfs中的 DefaultUrlTypes 示例),或者b)使用其他方法获取 URL(请查看 ClasspathHelper 中的其他方法并检查返回的 URL 列表)。

对于第二种情况,你应该a)将 customClassLoader 传递给 Reflections 构造函数或 ConfigurationBuilder,以便进行解析,或者b)直接查询存储 reflections.getStore().get(TypeAnnotationsScanner.class)

另请参见 #8339845JbossIntegration


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接