我正在为一个需要在初始化期间加载配置文件的项目创建一个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()完全相同(为了比较值,它是我维护的)!
这里发生了什么事?!
为什么直接调用方法不工作,而插入一个新的方法调用之间的工作呢?
老实说,面对这件事我真的很惊讶。
顺便说一下,我使用的是JUnit版本4.10。它可能是在某种程度上篡改了getClassLoader调用吗?
非常感谢,
卡尔斯
发布于 2014-10-29 11:23:42
回答你的问题
如果我调试它,我可以清楚地看到这个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");
时,您可以为您删除它。
发布于 2014-11-03 04:29:04
来自getResourceAsStream
对象的ClassLoader
函数不会删除与搜索字符串相加的斜杠,因为搜索将相对于类路径进行。即搜索用于加载类的搜索路径的资源。
例如,如果您的类yourpackage/Test.class
(位于/a/b/c/d/yourpackage/Test.class
下)由系统类加载器(即默认类加载器)加载,那么类路径必须指向/a/b/c/d
才能加载该类。搜索将在这条道路上完成。
类对象中的getResourceAsStream
函数确实删除了与搜索字符串相加的斜杠,因为搜索将相对于它所在的类进行。即搜索从何处加载类的资源。
例如,如果yourpackage/Test.class
从/a/b/c/d/yourpackage/Test.class
加载,那么资源路径将是/a/b/c/d/yourpackage/config/config.xml
您可以使用下面的代码片段来测试这一点,因为getResource
和getResourceAsStream
用于相同的搜索算法。
System.out.println(Test.class.getClassLoader().getResource("config/config.xml"));
System.out.println(Test.class.getResource("config/config.xml"));
发布于 2013-07-18 10:30:46
我不确定这一点,但是您可以尝试将资源文件夹放在src/main树中,而不是src/test树中。至少在某些配置中,eclipse将“资源”文件从src复制到类,而不是从test复制到类。值得一试..。
https://stackoverflow.com/questions/17730005
复制