1. 1.  前言

Java应用中很常见的一个问题,如何读取jar/war包内和所在路径的配置文件,不同的人根据不同的实践总结出了不同的方案,但其他人应用却会因为环境等的差异发现各种问题,本文则从原理上解释最佳实践。

  1. 2.  参考方案

2.1.log4j

log4j读取配置的代码是:

PropertyConfigurator.configure(“log4j.properties”);

实际执行的文件读取是:

FileInputStream istream = new FileInputStream(configFileName);

也就是执行环境${PWD}中查找文件,这个路径与new File()的路径是一样的。

2.2.hibernate

hibernate读取配置的代码是:

Configuration configuration = new Configuration().configure(“hibernate.cfg.xml”);

实际执行的文件读取是:

String stripped = resource.startsWith(“/”) ? resource.substring(1) : resource;

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

InputStream stream = classLoader.getResourceAsStream(stripped);

是通过类加载器的相对路径来查找文件,这种方式好处是可以读取jar中的文件,而且是new File()无法模拟的。

  1. 3.  方案分析

3.1.方案对比

相对而言,hibernate的文件读取更加实用,主要因为可以读取普通文件,也可以读取jar/war中的文件,相对更通用。

3.2.参考文档

更常见的情况是使用Class.getResourceAsStream(String name),实际上最终会调用ClassLoader.getSystemResourceAsStream,但很多人对于getResourceAsStream并不完全理解,所以很疑惑有些时候需要在路径上加上“/”,有些时候不用。

简单来说ClassLoader的基础路径是固定,而Class的基础路径则不相同,所以ClassLoader都是相对路径,Class读取则存在绝对路径和相对路径两种情况。

Class.getResourceAsStream文档中有解释:

(1)如果name以“/”开头,则是读取${PWD}下的文件。应用与读取jar同级别路径的文件如hibernate.cfg.xml。

(2)如果name不以“/”开头,则是读取Class所在路径package下的文件。应用于读取与Class有关的文件如*.hbm.xml。

  1. 4.  实践应用

4.1.最优方案

由于Class.getResourceAsStream会继续调用ClassLoader的方法,那么建立一个单例的ClassLoader是最优的方案,比如hibernate的实现。

结合maven来说,src/main/resources中的文件都使用ClassLoader.getSystemResourceAsStream(“log4j.properties”)方式读取,对于*.hbm.xml等ORM映射文件等则需要加上包路径,如“com/zheezes/*.hbm.xml”。

4.2.最佳实践

不过由于getClass()方式更简洁,而且性能损失非常小,所以实际使用中,直接调用getClass().getResourceAsStream()的方式更为常用。

 

结合maven来说,src/main/resources中的文件都使用绝对路径getClass().getResourceAsStream(“/log4j.properties”)方式读取,对于*.hbm.xml等ORM映射文件则一般在PO的Class中直接使用相对路径“*.hbm.xml”来读取。