在开发 WEB 运用的时候, 我常常忍受不了看到在 src 根目录底下放一堆配置文件, 在 WEB-INF 下又放一堆配置文件,
在某个比较隐藏的地方又存放了一些个配置文件, 这样总感觉很凌乱, 不方便也不好管理。
我更加倾向于使用 Maven 式的风格, 尽可能的通过 Source Folder 或 package 来管理项目的配置文件。
例如:
或者
其中, 资源文件放在 resources 根目录底下与放在 src 根目录底下是等效的, 因为在 Web Project 里面,
凡是 Source Folder 里面的东西最终都会编译到本地目录的 WebRoot/WEB-INF/classes 底下。
[ java 文件编译成了 .class 字节码文件存在于 classes, 非 java 文件则是直接拷贝搬过去。]
[ 如果是 Java Project, 则存在于项目所在的本地目录的 bin 目录底下, 实际上它们都是项目的类搜索路径。 ]
另外, 放在 package 里面的资源文件, 同样也会被编译(非 java 文件直接拷贝)到 classes 中, 存在于的目录与包名相对应。
由于资源文件放在 resources 与放在 src 底下效果是一致的, 所以接下主要是谈谈资源文件放在 package 下的情况。
以上面贴出来的右边的图为例, 资源文件放在 package 里面, 有时候常常需要自己去读取配置文件, 本人常用的手段有:
1> 通过 Class 类的 getResourceAsStream(String name) 方法来构造 InputStream。其中, name 的写法有两种: 绝对路径和相对路径。
1.1> 绝对路径:
1 public void app() throws IOException { 2 InputStream in = getClass().getResourceAsStream("/min/snail/resources/config.properties"); 3 System.out.println(in.available()); // 字节数 4 }
绝对路径的写法是以 '/' 开头: /modified_package_name/name
modified_package_name 指的是修正后的包名, 简单的理解就是包名, name 是资源文件的名称(包括后缀名)。
[ 包名中的 '.' 分隔符在转换成路径来使用的时候, 通通需要将 '.' 改写成 '/' 或 '\\', 以下说的包名皆是这个意思。 ]
1.2> 相对路径:
1 public void app() throws IOException { 2 InputStream in = getClass().getResourceAsStream("./resources/config.properties"); 3 System.out.println(in.available()); // 字节数 4 }
其中, app() 方法是写在 min.snail.Application 类里面。
'.' 代表的是当前 Class 对象的包名(以绝对路径形式), 在示例中相当于 /min/snail, 然后拼上后面的 resources/config.properties 就能找到资源文件。
也可以将 '.' 省略, 相对路径直接写成:
1 public void app() throws IOException { 2 InputStream in = getClass().getResourceAsStream("resources/config.properties"); 3 System.out.println(in.available()); // 字节数 4 }
1.3> 假设 app() 方法不是写在 min.snail.Application 类, 而是写在 min.snail.copy.Application 类里面, 那么相对路径是:
1 public void app() throws IOException { 2 InputStream in = getClass().getResourceAsStream("./../resources/config.properties"); 3 System.out.println(in.available()); 4 }
根据 1.2 的分析, 这里的 '.' 表示的是: /min/snail/copy
'..' 相信大家都知道是什么意思, 就是返回到上一级目录, 当前目录是 /min/snail/copy, 那么上一级目录就是 /min/snail
再拼上后面的 /resources/config.properties 就是: /min/snail/resources/config.properties
或者直接写成:
1 public void app() throws IOException { 2 InputStream in = getClass().getResourceAsStream("../resources/config.properties"); 3 System.out.println(in.available()); 4 }
通过 Class 类的 getResourceAsStream(String name) 局限性比较大, 因为只能利用它来构造输入流, 如果想构造输出流, 那么这种方法就显得无能为力了。
2> 通过 FileInputStream(String name) 来构造 InputStream。
FileInputStream(String name) 在底层其实是通过 FileInputStream(File file) 构造方法来实现的, 因此会去调 new File(String pathname)。
首先是要知道如何去写 name 的值, 为解决这一问题, 下面是借助 File 类来帮助理解:
示例一:
1 public void app() { 2 File file = new File(""); 3 System.out.println(file.exists()); //false 4 System.out.println(file.isFile()); //false 5 System.out.println(file.isDirectory()); //false 6 System.out.println(file.getAbsolutePath()); //F:\Workspace\X64\MyEclipse8.6\min-snail 7 }
示例二:
1 public void app() { 2 File file = new File("."); 3 System.out.println(file.exists()); //true 4 System.out.println(file.isFile()); //false 5 System.out.println(file.isDirectory()); //true 6 System.out.println(file.getAbsolutePath()); //F:\Workspace\X64\MyEclipse8.6\min-snail\. 7 }
示例一在构造 File 对象的时候传的是空串, 因此打印 file 不存在, file 不是一个文件, 也不是一个目录,
但此时 file 取得的绝对路径是项目所在磁盘的本地目录的路径;
示例二中, 结合示例一, 空串能取得项目所在本地磁盘的绝对路径, '.' 代表的是当前目录, 因此打印输出 file 存在,
file 不是一个文件而是一个目录, file 取得的绝对路径是项目所在磁盘的本地目录的路径后面加了个 '.', 这里的 '.' 对路径是
没有影响的, 它代表的是它前面的那段串所构成的路径。
[ File(String pathname) 构造方法在底层是借助 native 方法来实现的, 我追踪不到源码, 因此,
以上关于 File 构造方法中的 '.' 的理解纯属个人看法, 如果有说的不对的地方, 欢迎广大网友指出。 ]
2.1> 从上面可以看出, 空串 "" 和 "." 取得的绝对路径都是项目的根目录所在的路径, 因此, 如何构造 FileInputStream(String name) 就有招可支了:
1 public void app() throws IOException { 2 InputStream in = new FileInputStream("./src/min/snail/resources/config.properties"); 3 System.out.println(in.available()); // 字节数 4 }
因为 config.properties 文件就是存放在项目所在目录的 src/min/snail/resources/ 下, 而 '.' 能取到项目根目录的绝对路径。
也可以直接写成:
1 public void app() throws IOException { 2 InputStream in = new FileInputStream("src/min/snail/resources/config.properties"); 3 System.out.println(in.available()); // 字节数 4 }
因为空串也能取得项目根目录的绝对路径, 我是这么理解的, 如果有说的不对的地方, 同样, 欢迎广大网友指出。
需要注意的是, 不能以 '/' 开头, 例如:
new FileInputStream("/src/min/snail/resources/config.properties");
或
new FileInputStream("/./src/min/snail/resources/config.properties");
上面的两种写法是错误的, 运行时程序会抛出 java.io.FileNotFoundException。
因为 FileInputStream(String name), 底层会去调 new File(name), 而 File("/") 得到的绝对路径是系统盘的根目录:
1 public void app() { 2 3 File file = new File("/"); 4 System.out.println(file.getAbsolutePath()); //F:\ 5 6 File file2 = new File("/src/min/snail/resources/config.properties"); 7 System.out.println(file2.getAbsolutePath()); //F:\src\min\snail\resources\config.properties 8 }
3> 通过 ClassLoader.getSystemResource(String name) 来获取资源文件的路径。
ClassLoader.getSystemResource(String name) 返回的是一个 URL 对象, 接着可以通过 URL 类的 getPath() 方法来获取指定的 name 的绝对路径。
3.1> 先来看 ClassLoader.getSystemResource("") 构造出来的路径:
1 public void app() { 2 String path = ClassLoader.getSystemResource("").getPath(); 3 System.out.println(path); // /F:/Workspace/X64/MyEclipse8.6/min-snail/WebRoot/WEB-INF/classes/ 4 }
从打印结果可以看出, 空串 "" 获取的是项目的 classes 目录, 也就是项目的类搜索路径, 上面已经说到, package 里面的东西会被编译到 classes 中,
因此可以这样来找到资源文件路径:
1 public void app() { 2 String path = ClassLoader.getSystemResource("min/snail/resources/config.properties").getPath(); 3 System.out.println(path); ///F:/Workspace/X64/MyEclipse8.6/min-snail/WebRoot/WEB-INF/classes/min/snail/resources/config.properties 4 }
另外, ClassLoader.getSystemResource("") 与 ClassLoader.getSystemResource(".")、
ClassLoader.getSystemResource("min/snail/resources/config.properties").getPath() 与
ClassLoader.getSystemResource("./min/snail/resources/config.properties").getPath()
的异同与上面所述是相类似的, 这里不再赘述。
同样需要注意的是, 不能以 '/' 开头, 以 '/' 开头表明是绝对路径的写法, java 会直接根据 '/' 后面的字符串来寻找路径, 如写成:
ClassLoader.getSystemResource("/min/snail/resources/config.properties").getPath()
或
ClassLoader.getSystemResource("/./min/snail/resources/config.properties").getPath()
程序在运行时都会抛出 java.lang.NullPointerException 异常。
正确示例:
1 public void app() throws IOException { 2 String path = ClassLoader.getSystemResource("min/snail/resources/config.properties").getPath(); 3 InputStream in = new FileInputStream(path); 4 System.out.println(in.available()); // 字节数 5 }
注意: ClassLoader.getSystemResource(name) 这种方式不允许路径中含有空格或中文, 否则同样抛出 java.lang.NullPointerException 异常。