0、写在前面的话
最近在做微信的公众号开发,因为调用微信接口失败的话是以各类错误码和错误信息返回的,在Github发现有位老哥整理成了xml(weixin/error.xml),索性也就想借鉴他的方式和这份xml,将内容读取存储到一个Map中,这样在遇到微信返回的错误码时就可以从Map中取出对应的具体错误信息。
在工具类中建立一个静态块,然后试图读取xml然后存入Map,问题来了,之前在Servlet中都是利用的ServletContext上下文对象,使用getRealPath(String path)方法获取文件路径。可是这个工具类并不是Servlet,也就没有ServletContext。
然而,什么都难不倒Google,下面就类似的配置文件的读取问题,进行一下总结。
另外,要说一个前提是,xml、properties等配置文件和Java资源都是放在WEB-INF下面的classes文件夹中(以下则称之为classpath)。我们的目的是如何获取该目录下的文件资源,假如我们现在的文件为应用根目录下" .../WEB-INF/classes/error.xml "。
1、在Servlet类中读取
假如要将文件获取为流,有两种读取方式:
(1)直接读取文件为流,getResourceAsStream(path)方法,path默认为应用的根目录
InputStream inputStream = this.getServletContext().getResourceAsStream("/WEB-INF/classes/error.xml");
1
1
InputStream inputStream = this.getServletContext().getResourceAsStream("/WEB-INF/classes/error.xml");
(2)先读取文件,再读取为流,获取路径的方式和上面是类似的
String path = this.getServletContext().getRealPath("/WEB-INF/classes/error.xml");
InputStream inputStream = new FileInputStream(path);
2
1
String path = this.getServletContext().getRealPath("/WEB-INF/classes/error.xml");
2
InputStream inputStream = new FileInputStream(path);
2、在非Servlet类中(普通Java类中)的读取
在非Servlet类中,要将文件获取为流,也有两种方式:
(1)用ClassLoader类加载资源文件,这里默认是从classes目录下读取
用ClassLoader加载配置文件时,path,也就是这里的 "error.xml" 不能以"/"开头,在查找时直接在classpath下进行查找:
InputStream in = ClassName.class.getClassLoader().getResourceAsStream("error.xml");
1
1
InputStream in = ClassName.class.getClassLoader().getResourceAsStream("error.xml");
同样的方法,也可以迂回一下,先读取到文件的路径,再用流读出,这里FileInputStream的path就可以使用绝对或者相对路径:
String path = ClassName.class.getClassLoader().getResource("error.xml").getPath();
InputStream inputStream = new FileInputStream(path);
2
1
String path = ClassName.class.getClassLoader().getResource("error.xml").getPath();
2
InputStream inputStream = new FileInputStream(path);
(2)用Class类加载资源文件,形参不同,访问路径不同
InputStream inputStream = ClassName.class.getResourceAsStream("/error.xml");
1
1
InputStream inputStream = ClassName.class.getResourceAsStream("/error.xml");
需要注意的是,这种方式的形参有两种方式:
- 绝对定位,“/”开头,此时即以classpath为根目录
- 相对定位,不加“/”,则以调用getResourceAsStream类的包路径作为根目录(即该类所在包下获取资源)
最后,也就是用了如上的方法,解决了我读取error.xml的问题:
//读取errorCode错误码到Map中
static {
InputStream xml = WeChatUtil.class.getResourceAsStream("/error.xml");
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(xml);
Element root = document.getRootElement();
List<Element> elements = root.elements();
for (Element element : elements) {
String code = element.elementText("code");
String text = element.elementText("text");
WeChatUtil.errorCodeMap.put(code, text);
}
} catch (DocumentException e) {
log.debug("error.xml读取失败");
e.printStackTrace();
}
}
1
//读取errorCode错误码到Map中
2
static {
3
InputStream xml = WeChatUtil.class.getResourceAsStream("/error.xml");
4
SAXReader saxReader = new SAXReader();
5
try {
6
Document document = saxReader.read(xml);
7
Element root = document.getRootElement();
8
List<Element> elements = root.elements();
9
for (Element element : elements) {
10
String code = element.elementText("code");
11
String text = element.elementText("text");
12
WeChatUtil.errorCodeMap.put(code, text);
13
}
14
} catch (DocumentException e) {
15
log.debug("error.xml读取失败");
16
e.printStackTrace();
17
}
18
}
3、参考链接
题外话:
在使用Spring时通常看到诸如 <param-value>classpath:applicationContext-*.xml</param-value> 的配置,此处的classpath确实是指WEB-INF下的classes目录,但此处毕竟只是配置的字符串,实际上获取资源路径时是框架进行了识别和处理的。我之前也是很愚,一直不理解所谓的classpath,今天才明白了些许,顺便追了下Spring源码。
在Spring的配置中,实际上有classpath和classpath*,区别在于:
- classpath 只会到你的classes路径中查找找文件
- classpath* 不仅包含classes路径,还包括jar文件中(class路径)进行查找