10

我正在使用 Google Reflections 库来查询类路径中的某些资源。这些资源与我项目中的类位于同一位置。

我编写了一些在 Eclipse 中作为单元测试执行时成功的单元测试,但是当我尝试使用 Maven(maven install例如)执行它们时,它们没有按预期工作。经过一番调试,显然问题是用Maven执行时,Reflections库找不到资源所在的classpath url。

我得出了这个结论,研究反射如何确定应该检查的类路径 URL。例如,以下方法显示了 Reflections 如何在给定类加载器的情况下找到可用的类路径 URL(原始的 Reflections 方法已被简化了一点):

public static Set<URL> forClassLoader(ClassLoader... classLoaders) {
    final Set<URL> result = Sets.newHashSet();
    for (ClassLoader classLoader : classLoaders) {
        while (classLoader != null) {
            if (classLoader instanceof URLClassLoader) {
                URL[] urls = ((URLClassLoader) classLoader).getURLs();
                if (urls != null) {
                    result.addAll(Sets.<URL>newHashSet(urls));
                }
            } 
            classLoader = classLoader.getParent();
        }
    }
    return result;
}

简而言之,它正在遍历类加载器层次结构,询问每个单独的类加载器的 URL。

在 Eclipse 中,我从单元测试中调用以前的方法,如下所示:

    ClassLoader myClassClassLoader = <MyClass>.class.getClassLoader(); //<MyClass> is in the same classpath url than the resources I need to find
    Set<URL> urls = forClassLoader(myClassClassLoader);
    for(URL url : urls) {
      System.out.println("a url: " + url);

正如预期的那样,我可以看到(在许多其他 URL 中)配置为我的项目的一部分的类路径 URL:

file:<MY_PROJECT_PATH>/target/classes/
file:<MY_PROJECT_PATH>/target/test-classes/

和 Reflections 是一种魅力(Reflections 应该找到的资源位于file:<MY_PROJECT_PATH>/target/classes/)。

但是,当测试由 Maven 执行时,我意识到这些 URL 条目从该forClassLoader方法返回的集合中丢失,并且其余的 Reflections 方法对于这个问题没有按预期工作。

“令人惊讶”的事情是,如果我在 maven 执行单元测试时写这个:

ClassLoader myClassClassLoader = <MyClass>.class.getClassLoader();
url = myClassClassLoader.getResource("anExistingResource");
System.out.println("URL: "+url); //a valid URL

我可以看到类加载器仍然可以解析我要查找的资源。我很困惑为什么当使用 Maven 执行该forClassLoader方法时,返回的集合中不包含我项目的类路径 URL,尽管同时它能够解析位于此类 url(!)中的资源。

这种行为的原因是什么?当作为 Maven 运行的单元测试的一部分调用时,我可以尝试使 Reflections 库工作的任何解决方法吗?

4

5 回答 5

7

解决了。发布解决方案,以防将来有人发现同样的问题。

在执行项目的单元测试时,Maven 不会(明确地)在类路径中包含其所有依赖项。相反,它声明了对位于“target/surefire/surefirebooter_NUMBER_THAT_LOOKS_LIKE_TIME_STAMP.jar”中的 tmp jar 的依赖。此 jar 仅包含一个清单文件,该文件声明项目的类路径。

Reflections 库中的方法forClassLoader不返回一组具有有效类路径的 url(即,清单文件中的类路径条目被忽略)。为了克服这个问题,我刚刚实现了这个简单的方法:

public static Set<URL> effectiveClassPathUrls(ClassLoader... classLoaders) {
    return ClasspathHelper.forManifest(ClasspathHelper.forClassLoader(classLoaders));
}

该方法forManifest(也是 Reflections 库的一部分)向作为参数发送的类路径 url 集添加了在该集中包含的任何 jar 文件的清单文件中声明的缺失类路径条目。通过这种方式,该方法返回一组带有项目有效类路径的 URL。

于 2012-11-27T18:52:18.987 回答
3

您可能正在使用 M2Eclipse,它会自行将内容添加到类路径中。命令行 Maven 的工作方式不同。您可能会找到一些有用的选项

于 2012-11-27T03:16:43.620 回答
2

我有同样的问题。添加以下 URL 对我有用。

ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setUrls(...);
cb.addUrls(YourClassName.class.getProtectionDomain().getCodeSource().getLocation());
于 2013-03-14T21:54:26.573 回答
2

我刚刚在使用反射库(版本 0.9.11)时遇到了同样的问题,只有在从 Maven 构建执行单元测试时。接受的答案中提供的链接为我指明了正确的方向。

对我的 Surefire 插件的简单 POM 文件更改解决了这个问题:

            <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.21.0</version>
            <configuration>
                <useSystemClassLoader>false</useSystemClassLoader>
            </configuration>
        </plugin>
        <plugin>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>2.21.0</version>
            <configuration>
                <useSystemClassLoader>false</useSystemClassLoader>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>integration-test</goal>
                        <goal>verify</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

<useSystemClassLoader>配置参数默认为“真” 。强制它为“假”似乎可以解决我的单元测试中的类加载器问题。

于 2018-06-06T12:58:09.780 回答
0

您可以创建一些导致surefire失败的问题。

  1. 有一个命名约定;测试套件应称为“TestBlaBla”或“BlaBlaTest”;以单词“Test”开头或结尾。

  2. 如前所述,Maven 中的类路径比 Eclipse 中更受限制,因为 Eclipse(愚蠢地)没有将编译类路径与测试类路径分开。

  3. Surefire 可以以任意顺序从不同的测试套件中自由运行测试用例。当运行多个测试套件初始化一些通用基础(例如内存数据库或 JNDI 上下文)时,可能会在测试套件开始相互影响时产生冲突。您需要注意正确隔离测试套件。我使用的技巧是为套件使用单独的内存数据库,并且我为每个单元测试而不是每个测试套件初始化共享的东西。

我可以告诉你,3 是最难调试的;每当某些东西在 Eclipse 中而不是在 Maven 中起作用时,我自然会认为我在隔离测试套件时做错了。

于 2012-11-27T09:21:23.363 回答