项目中经常有很多配置文件,却很少关心其位置。配置文件规范(命名、位置)取决于其读取方式,源码中常有一些loader来加载这些配置文件。配置文件的命名通常是固定的(xxx.properties,xxx.yml,xxx.xml),而loader加载配置文件location也有规范,常见“/”、“.”、“classpath”、“file”。
- / 根目录 (磁盘根目录)
- . 运行class文件的命令目录(一般为项目根目录)
- classpath class文件目录
- file file路径(相当于new file)
在使用时file:与new file用法一致,而classpath 与"."或"/"或""联合使用都指classes目录,而"."与""表达式同一个意思,项目根目录或jar同级目录,"/"表示磁盘根目录,实际中使用"."表示相对路径更多。此外需要区分classpath*与classpath,能使用classpath就不要使用classpath*(会加载jar中文件)。测试代码如下:
/**
* 1.项目更目录有 t.properties文件,打包后jar同级目录有 t.properties
* 2.classes目录有application.properties(打包后也是)
*
*/
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
PropertySourcesLoader propertySourcesLoader = new PropertySourcesLoader();
// 磁盘根目录
File file = new File("/");
System.out.println(file.getAbsolutePath());
// 项目根目录(jar同级目录)
file = new File(".");
System.out.println(file.getAbsolutePath());
System.out.println("############classpath###########");
// classes目录
System.out.println(propertySourcesLoader.load(resourceLoader.getResource("classpath:/application.properties"))
.getProperty("server.port"));
System.out.println(propertySourcesLoader.load(resourceLoader.getResource("classpath:./application.properties"))
.getProperty("server.port"));
System.out.println(propertySourcesLoader.load(resourceLoader.getResource("classpath:application.properties"))
.getProperty("server.port"));
//ResourceLoader 默认的classpath
System.out.println(propertySourcesLoader.load(resourceLoader.getResource("application.properties"))
.getProperty("server.port"));
System.out.println(propertySourcesLoader.load(resourceLoader.getResource("./application.properties"))
.getProperty("server.port"));
System.out.println(propertySourcesLoader.load(resourceLoader.getResource("application.properties"))
.getProperty("server.port"));
System.out.println("############file###########");
// 项目根目录(jar同级目录)
file = resourceLoader.getResource("file:t.properties").getFile();
System.out.println(file.getAbsolutePath());
file = resourceLoader.getResource("file:./t.properties").getFile();
System.out.println(file.getAbsolutePath());
file = resourceLoader.getResource("file:/t.properties").getFile();
System.out.println(file.exists());// false
// 磁盘根目录
file = new File("t.properties");
System.out.println(file.getAbsolutePath());
file = new File("./t.properties");
System.out.println(file.getAbsolutePath());
file = new File("/t.properties");
System.out.println(file.exists());// false
代码加载大多通过加载器加载,常用ResourceLoader默认classpath目录,在项目打包时,需要将配置文件打在jar包外面时,即可使用"file:"路径,比如springboot的application配置配置默认加载"classpath:/","classpath:/config/","file:./","file:./config/"(详见DefaultResourceLoader)。当然,项目在配置文件上读取更为灵活,可以指定自定义的配置文件,如springboot会从环境中读取spring.config.location所配置的属性文件。属性文件不要进行重复配置,在不清楚其加载顺序的情况下,属性覆盖会带来意想不到的问题(有些jar中有同名的配置文件,而配置时又使用了classpath*)。
资源加载器ResourceLoader跟类加载器Classloader是有区别。ResourceLoader相对简单,从给定的路径中加载,而Classloader则相对复杂,Classloader主要用于加载Class,但也可以通过指定路径加载文件。
//当前class目录
System.out.println(Res.class.getResource("").getFile());
System.out.println(Res.class.getResource(".").getFile());
//classes目录
System.out.println(Res.class.getResource("/").getFile());
需要额外一提的是,打包之后,在jar同会生成一个MANIFEST.MF文件,里面指定Main-class(java -jar 执行入口),也有class-path,指定jar的的依赖关系,class、 loader会根据这个路径来搜索class。