【问题标题】:Reflections returning Set with null elements反射返回带有空元素的 Set
【发布时间】:2013-04-05 19:30:23
【问题描述】:

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

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

war 包含的一个 JAR 包,它有一个执行此代码的类:

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

JAR 包中也存在 CustomAnnotation。

设置大小是正确的(即,如果我的 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)。

【问题讨论】:

  • 您的注释类是否用@Retention(RetentionPolicy.RUNTIME) 标记?
  • @better_use_mkstemp 是的
  • multimap 'reflections.getStore().get(TypeAnnotationsScanner.class)' 是否包含您想要的内容?你在使用 osgi 容器吗?

标签: java reflection reflections


【解决方案1】:

我想我不能使用反射库来做到这一点。所以我亲手做了:

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 类以公开“类”属性。例如,Java 9 中可能不存在此属性,因此请注意。如果您正在与第三方编写的模块进行交互,这也可能存在一些安全问题。

【讨论】:

  • 喜欢你的答案 - 提供反射 API 的全部功能,并且可以在未来被重用/黑客用于许多其他目的。但这是 wrm 的 cmets 所描述的解决方案,所以我希望你给予 wrm 而不是你自己的赏金。干杯:^)
  • 'classLoaderClass.getDeclaredField("classes")' 有问题
  • @zapp 我知道这不是一个好的解决方案,但为什么这是一个坏主意?
【解决方案2】:

我有一个类似的problem。你确定,你将注释类包含在你的类路径中吗?如果它们没有加载,它们会以某种方式被找到但不会真正返回,并且没有任何异常或任何东西

【讨论】:

  • 是的,注释在那里。我实际上有一个在 JAR 包上使用它的类,我可以得到那个类,但是 WAR 包上的类返回 null。
  • 对我来说仍然听起来像一个类路径问题....您是否尝试过删除“反射”并使用直接 java-reflection 来列出您的类上的注释?也可能是不同的类加载器加载了注释类。即war和jar使用了不同的类加载器。
  • 我怀疑类加载器是问题所在,但我对 Java 中的类加载器和反射不是很有经验,所以我不完全确定。如果不使用反射库,我将如何做到这一点?
  • 您知道如何从 JAR 类中获取 WAR 类加载器吗?我将我的应用程序打包为 EAR,其中包含 WAR 和 JAR。
  • 您可以使用 getClass().getClassLoader().loadClass(...) 加载您的类,然后像这样提取您的注释:tutorials.jenkov.com/java-reflection/annotations.html#class 您可以尝试使用 threadclassloader 来查看,如果它工作......(使用Thread.currentThread().getContextClassLoader()
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    您的代码应该可以在常规环境中运行。 但是,在不同的环境中,比如 osgi,你会得到:

    1) 不同协议的 url (bundle/vfs/...)

    2) 不同的类加载器。

    在第一种情况下,您应该a)添加相关的UrlType(示例参见Vfs中的DefaultUrlTypes),或b)使用不同的方法获取url(参见 ClasspathHelper 中的其他方法并检查返回的 URL 列表)

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

    另见#8339845JbossIntegration

    【讨论】:

      猜你喜欢
      • 2020-11-18
      • 2019-01-10
      • 1970-01-01
      • 1970-01-01
      • 2011-03-07
      • 2014-09-16
      • 1970-01-01
      • 1970-01-01
      • 2015-01-23
      相关资源
      最近更新 更多