Java 从JUnit获取资源流

Java 从JUnit获取资源流,java,junit,classloader,getresource,Java,Junit,Classloader,Getresource,我正在为需要在初始化期间加载配置文件的项目创建JUnit测试用例 此配置文件位于src/main/resources/config文件夹中的项目中,在构建期间,maven将其放入JAR中的/config文件夹中 初始化类使用以下语句从那里读取文件: ClassLoader classloader = this.getClass().getClassLoader(); BufferedReader xmlSource = new BufferedReader(new InputStreamRead

我正在为需要在初始化期间加载配置文件的项目创建JUnit测试用例

此配置文件位于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测试用例中运行它时,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测试中使用它呢

更新

我刚刚提出了解决方案,对代码做了一个小改动: 我没有使用类加载器获取资源,而是直接使用类:

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()完全相同(我维护了它,只是为了比较值)

这是怎么回事

为什么直接调用方法不起作用,而在两者之间插入一个新的方法调用起作用

老实说,我真的很惊讶

顺便说一句,我使用的是JUnit4.10版。它可能以某种方式篡改了getClassLoader调用吗

非常感谢,


Carles

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

回答你的问题

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

这是怎么回事

为什么直接调用方法不起作用,而在两者之间插入一个新的方法调用起作用

打电话和打电话的区别

this.getClass().getClassLoader().getResourceAsStream("/config/config.xml");
和呼唤

this.getClass().getResourceAsStream("/config/config.xml");
位于您在
类中显示的确切来源中:

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)
。这是
类中的私有方法

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;
}
私有字符串解析名称(字符串名称){
if(name==null){
返回名称;
}
如果(!name.startsWith(“/”){
c类=这类;
而(c.isArray()){
c=c.getComponentType();
}
字符串baseName=c.getName();
int index=baseName.lastIndexOf('.');
如果(索引!=-1){
name=baseName.substring(0,索引).replace('.','/'))
+“/”+姓名;
}
}否则{
name=name.substring(1);
}
返回名称;
}
因此,您可以看到,在实际调用类加载器的
getResourceAsStream()
之前,它实际上从路径中删除了起始斜杠

一般来说,当它没有斜杠时,它会尝试获取与
this
相关的资源,如果它在开始时有斜杠,则将其传递给类加载器

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


所以当您使用
this.getClass().getClassLoader().getResourceAsStream(“/config/config.xml”),实际上是在开始处用斜杠传递路径,但失败了。当您使用
this.getClass().getResourceAsStream(“/config/config.xml”)
为您删除它真是太好了。

类加载器中的getResourceAsStream
函数将不会删除搜索字符串前面的斜杠,因为搜索将相对于类路径进行。i、 e搜索用于加载类的搜索路径所在的资源

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


getResourceAsStream
类对象中的函数确实会删除搜索字符串前面的斜杠,因为搜索将相对于它所在的类进行。i、 e搜索从中加载类的资源

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

您可以使用以下代码snipet来测试这一点,因为
getResource
getresourceastream
都使用相同的搜索算法

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

src/test/resources/
是Eclipse中的源文件夹吗?@Sotirios