来自JUnit的getResourceAsStream

时间:2013-07-18 17:16:57

标签: java junit classloader getresource

我正在为需要在初始化期间加载配置文件的项目创建一个JUnit TestCase。

此配置文件位于src / main / resources / config文件夹中的项目内,并且在构建maven期间将其放入JAR内的/ config文件夹中。

初始化类,使用以下语句从那里读取文件:

ClassLoader classloader = this.getClass().getClassLoader();
BufferedReader xmlSource = new BufferedReader(new InputStreamReader(classLoader.getResourceAsStream("/config/config.xml")));

我遇到的问题是,当我将这个jar部署并执行到应用程序服务器时,它按预期工作,但是,每当我在Eclipse中的JUnit TestCase中运行它时,getResrouceAsStream方法都返回null。

考虑到该类是my.package.MyClassTest.java,并且它位于src / test / java / my / package / MyClassTest.java中,我已经尝试将config.xml文件的副本放入以下文件夹没有成功:

- src/test/resources/config
- src/test/resources/my/package/config
- src/test/java/my/package/config

我知道在StackOverflow中已经多次询问过类似的问题,但我发现的所有响应都指的是改变文件的加载方式,虽然更改代码可能是一个选项,但我更愿意找到该文件的正确位置,所以我不需要修改已在生产环境中工作的东西。

那么,我应该在哪里放置这个文件以便能够在我的JUnit测试中使用它?

更新

我只是在代码中进行了一些小改动,想出了解决方案: 我没有使用ClassLoader来获取资源,而是直接使用了类:

Class clazz = this.getClass();
BufferedReader xmlSource = new BufferedReader(new InputStreamReader(clazz.getResourceAsStream("/config/config.xml")));

它从src / test / resources / config / config.xml成功读取文件。

然而,这里有一些非常奇怪的东西: Class.getResourceAsStream方法是:

public InputStream getResourceAsStream(String name) {
    name = resolveName(name);
    ClassLoader cl = getClassLoader0();
    if (cl==null) {
        // A system class.
        return ClassLoader.getSystemResourceAsStream(name);
    }
    return cl.getResourceAsStream(name);
}

如果我调试它,我可以清楚地看到这个 getClassLoader0()返回与前一个调用完全相同的对象(相同的id), this.getClass()。getResourceAsStream( )(我维护,只是为了比较值)!!!

这里发生了什么?!

为什么在工作之间插入新方法调用时,直接调用方法不起作用?

老实说,我对此感到非常惊讶。

BTW,我使用的是JUnit 4.10版。可能它以某种方式篡改了getClassLoader调用吗?

非常感谢,

卡尔斯

3 个答案:

答案 0 :(得分:17)

回复你的问题

  

如果我调试它,我可以清楚地看到这个 getClassLoader0()返回与前一个调用完全相同的对象(相同的id), this.getClass()。getResourceAsStream( )(我维护,只是为了比较值)!!!

     

这里发生了什么?!

     

为什么在工作之间插入新方法调用时,直接调用方法不起作用?

调用

之间的区别
this.getClass().getClassLoader().getResourceAsStream("/config/config.xml");

并致电

this.getClass().getResourceAsStream("/config/config.xml");

位于Class显示的确切来源:

public InputStream getResourceAsStream(String name) {
    name = resolveName(name);
    ClassLoader cl = getClassLoader0();
    if (cl==null) {
        // A system class.
        return ClassLoader.getSystemResourceAsStream(name);
    }
    return cl.getResourceAsStream(name);
}

但问题不在于getClassLoader0()返回的问题。它在两种情况下都返回相同的内容。差异实际上在resolveName(name)。这是Class类中的私有方法。

private String resolveName(String name) {
    if (name == null) {
        return name;
    }
    if (!name.startsWith("/")) {
        Class<?> c = this;
        while (c.isArray()) {
            c = c.getComponentType();
        }
        String baseName = c.getName();
        int index = baseName.lastIndexOf('.');
        if (index != -1) {
            name = baseName.substring(0, index).replace('.', '/')
                +"/"+name;
        }
    } else {
        name = name.substring(1);
    }
    return name;
}

所以你看,在实际调用classLoader的getResourceAsStream()之前,实际上从路径中删除了起始斜杠

一般情况下,它会尝试在没有斜杠的情况下获取相对于this的资源,如果它在开头有斜杠,则将其传递给classLoader。

classLoader的getResourceAsStream()方法实际上用于相对路径(否则你只需使用FileInputStream)。

所以当你使用this.getClass().getClassLoader().getResourceAsStream("/config/config.xml");时,你实际上是在路径中用斜杠传递它,但失败了。当您使用this.getClass().getResourceAsStream("/config/config.xml");时,它很适合为您删除它。

答案 1 :(得分:2)

来自getResourceAsStream对象的

ClassLoader函数不会删除搜索字符串前面的斜杠,因为搜索将相对于类路径进行。即搜索用于加载类的搜索路径的资源。

例如,假设您的类yourpackage/Test.class位于/a/b/c/d/yourpackage/Test.class下,由系统类加载器(即默认类加载器)加载,并且您的类路径必须指向/a/b/c/d才能加载类。搜索将在此路径上完成。

Class Object中的

getResourceAsStream函数会删除搜索字符串前面的斜杠,因为搜索将相对于它所在的类进行。即搜索正在加载类的资源。

例如,如果从yourpackage/Test.class加载/a/b/c/d/yourpackage/Test.class,那么资源路径将为/a/b/c/d/yourpackage/config/config.xml

您可以使用以下代码snipet对此进行测试,因为getResourcegetResourceAsStream过去都使用相同的搜索算法。

System.out.println(Test.class.getClassLoader().getResource("config/config.xml"));
System.out.println(Test.class.getResource("config/config.xml"));

答案 2 :(得分:1)

我不确定这一点,但您可以尝试将您的资源文件夹放在src / main树而不是src / test树中。在某些配置中,至少eclipse将“资源”文件从src复制到类,而不是从测试复制到类。这值得一试......