最近读取 resources
下面的文件,竟然都是 null
,所以,对这个现象进行了调查。
今天,遇到了一个问题:使用 Class
类对象的 getResourceAsStream
方法读取文件时,返回值为 null
,即找不到文件,这究竟是怎么回事呢?
假设有一个 Maven
(或者 Gradle
)项目,在其 src/main/resources
目录下有一个文件叫作 generatorConfig.xml
(Mybatis Generator
的配置文件),项目结构如下:
1 | project |
任务目标是读取 generatorConfig.xml
文件。当时,我信心满满地写下了如下代码:
1 | import java.io.InputStream; |
结果却为 true
,也就是说,压根没找到这个文件。
问题分析
后来,我发现是自己没有理解 Class::getResourceAsStream
方法的使用规则。
查看注释
实际上,在 Class::getResourceAsStream
的源码中可以看到如下注释:
This method delegates to this object’s class loader. If this object was loaded by the bootstrap class loader, the method delegates to ClassLoader.getSystemResourceAsStream.
Before delegation, an absolute resource name is constructed from the given resource name using this algorithm:
If the name begins with a ‘/‘ (‘\u002f’), then the absolute name of the resource is the portion of the name following the ‘/‘.
Otherwise, the absolute name is of the following form:
modified_package_name/name
Where the modified_package_name is the package name of this object with ‘/‘ substituted for ‘.’ (‘\u002e’).
大意是说:
- 如果
name
以/
开头,则文件的绝对名称就是/
后面的部分; - 否则,文件的绝对名称就是当前对象所在的包路径,加上文件名。
源码分析
看完注释,不妨再阅读下源码,可以有更加清楚的认识。
Class::getResourceAsStream
的源码如下
1 | public InputStream getResourceAsStream(String name) { |
resolveName
方法的源码如下:
1 | private String resolveName(String name) { |
通过上述分析,可以得到以下的结论:假设类Main的包名为 com.hegongshan
,要读取的文件名为 generatorConfig.xml
。此时,如果不加前缀 /
,那么 Class::getResourceAsStream
方法会以为要读取的是 com/heogngshan/generatorConfig.xml
。
解决方案
明白问题所在之后,要解决该问题就非常容易了。
使用 Class::getResourceAsStream
方法,并在文件名前加上 /
1 | import java.io.InputStream; |
使用 ClassLoader::getResourceAsStream
方法
1 | import java.io.InputStream; |
使用 ClassLoader::getSystemResourceAsStream
方法
1 | import java.io.InputStream; |