目录
Properties 持久化资源属性
ResourceBundle 读取资源文件内容
ClassLoader 类加载器
读取路径中文乱码问题
getClass().getResourceAsStream() 获取输入流
Spring StreamUtils 直接读取资源内容到字符串
Spring ApplicationContex 访问资源
ResourceUtils 资源文件工具类
ClassPathResource 类路径资源
Hutool Setting
Spring ResourceLoader 接口访问资源文件
PathMatchingResourcePatternResolver-资源模式解析器 批量读取类路径文件资源
Spring Boot yml 文件读取方式汇总
Properties 持久化资源属性
1、Properties 类表示一组持久的属性,Properties 可以保存到流中或从流中加载,属性列表中的每个键及其对应的值都是一个字符串。
class java.util.Properties extends Hashtable<Object,Object>
2、一个属性列表可包含另一个属性列表作为它的“默认值”,如果未能在原有的属性列表中搜索到属性键,则搜索第二个属性列表。
3、Properties 继承于 Hashtable,所以可对 Properties 对象应用 put 和 putAll 方法,但不建议使用这两个方法,因为它们允许调用者插入其键或值不是 String 的项,推荐使用 setProperty 方法。
4、Properties 类是线程安全的,多个线程可以共享一个 Properties 对象,而不需要外部同步。因为它的关键方法上加了 synchronized 关键字。
一言以蔽之:java.util.Properties 是用于快捷操作 *.properties 、*.xml 等文件工具类,可以对它们进行增删改查,持久化到磁盘。
Properties 属性增删改查
1、注意1:.properties 属性文件一行代表一条数据、结尾不能有分号、不支持中文、#开头表示注释,使用 key=value。格式如下所示:
#.properties 资源文件格式就是简单的 key=value 形式,一行写一个 key-value,换行分割,结尾不要有分号
name=root
password=123456
#资源文件中不推荐有中文,如果 key 有多个 value,可以选择使用特定的分割符连接,代码中再进行 split(分割)
color=red,yellow,blue
2、注意2:项目打包成 .jar、.war 包部署时,此时类路径文件被嵌套在 .jar、.war 包文件内部,此时资源文件内容只读,无法增删改,否则报错:java.io.FileNotFoundException: file:/x/target\x-SNAPSHOT.jar!\BOOT-INF\classes!\x.x (文件名、目录名或卷标语法不正确。)
3、Properties 读取时会自动忽略注释。
在线演示源码: |
ResourceBundle 读取资源文件内容
1、public abstract class java.util.ResourceBundle 主要用于读取类路径下的 *.properties 资源文件,实现语言国际化。
2、比如全世界都在用 weChat(微信),那么就需要有一个功能,对日本人显示的是日本文,对韩国人显示的是韩文,英国人显示英文,中国人显示中文,而 ResourceBundle 资源绑定就是解决这个问题,通过绑定不同的资源文件(即语言)来达到不同语言的显示。
3、ResourceBundle 直接子类:ListResourceBundle , PropertyResourceBundle。
4、单纯就读取 *.properties 资源文件内容,ResourceBundle 比 Properties 更加方便快捷,但是 Properties 功能更强大。
5、ResourceBundle 是抽象类,使用 public static final ResourceBundle getBundle(String baseName) 方法创建实例,baseName 是 classpath 下的 xxx.properties 的文件名称,不用带后缀名 ".properties"。
6、ResourceBundle 常用方法如下:
public static final void clearCache():从使用调用者的类加载器加载的缓存中删除所有资源绑定
public static final void clearCache(ClassLoader loader):从使用给定的类加载器加载的缓存中删除所有资源绑定
public boolean containsKey(String key):确定给定的 key是否包含在此 ResourceBundle或其父包中。如果 key 为 null,则抛异常。
public String getBaseBundleName():如果已知,则返回此包的基本名称,如果未知,则返回 null
public static final ResourceBundle getBundle(String baseName):根据资源文件名(不含后缀)创建实例
public static final ResourceBundle getBundle(String baseName,Locale locale):根据资源文件名以及区域设置(Locale) 创建实例,这是做国际化的常用方法。
public abstract Enumeration<String> getKeys():返回键的枚举
public final String getString(String key):从此资源包或其父项之一获取给定 key 的值。//如果 key 不存在,则抛出异常
public final Object getObject(String key):从此资源束或其父项之一获取给定 key 的值。//如果 key 不存在,则抛出异常
@GetMapping("resourceBundle/findAllByClassPath")
public Map<String, String> findAllByClassPath(String classPath) {
Map<String, String> stringMap = new HashMap<>();
classPath = StrUtil.isBlank(classPath) ? "config/config2" : classPath;
// 如果资源文件不是在类路径根目录下,则使用 "/" 或者 "." 符号,如 data/config、data.config 都可以,但是不要再写后缀名 '.properties'
// 如果类路径下找不到指定的属性文件,则抛出异常 MissingResourceException: Can't find bundle for base name xxx
ResourceBundle resourceBundle = ResourceBundle.getBundle(classPath);
// value 可以不存在,但是如果 key 为 null 或者不存在,则抛出异常。可以使用 containsKey(String key) 先判断是否 key 存在
String name = resourceBundle.getString("name");
String password = resourceBundle.getString("password");
String color = resourceBundle.getString("color");
String[] colors = resourceBundle.getString("color").split(",");
Enumeration<String> enumeration = resourceBundle.getKeys();
System.out.println("name=" + name + ", password" + password + ", color=" + color);
System.out.println(Arrays.asList(colors));
System.out.println("===============");
while (enumeration.hasMoreElements()) {
String elementName = enumeration.nextElement();
String elementValue = resourceBundle.getString(elementName);
System.out.println(elementName + "=" + elementValue);
stringMap.put(elementName, elementValue);
}
// {"password":"123456","color":"red,yellow,blue","name":"root"}
return stringMap;
}
在线演示源码: |
ClassLoader 类加载器
1、java.lang.ClassLoader 类是一个抽象类,负责加载类的对象。如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。
2、ClassLoader 类使用委托模型来搜索类和资源。每个 ClassLoader 实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。
3、 类加载器读取:读取类路径下任意位置中的任意资源,可以直接拿到文件的输入流,或者文件的绝对地址url,即可以读又可以写。
4、默认就是相对类路径下,所以路径前缀不要加 /。主要就是下面两个方法,获取到 URL或者输入流之后,就可以使用各种合适的方式读写内容了。
URL url = classLoader.getResource("data/config.properties");
InputStream inputStream = classLoader.getResourceAsStream("data/config.properties");
在线演示源码: |
读取路径中文乱码问题
1、ClassLoader.getResource 方法内部使用 utf-8 对路径信息进行了编码,当路径中存在中文或空格等字符时,就会对它们进行转换,这样获取路径信息时就会出现 utf-8 编码后的字符,如 '85%ac%e5%bc%80%e9%85%8d%e7%bd' ,看起以来就像乱码一样,导致路径不存在。
2、解决办法是使用 URLDecoder.decoder 方法对地址进行解码,以得到原始路径。
getClass().getResourceAsStream() 获取输入流
/**
* 可以直接使用 getClass().getResourceAsStream() 方法,它 会返回一个 InputStream 对象。
* 如果此对象是由引导类加载器加载的,则该方法将委托给{@link ClassLoader#getSystemResourceAsStream}。
* http://localhost:8080/class/getResourceAsStream
* <p>
* 即便是打包成 .jar、.war 包部署到生产环境,照样读取正常。
*
* @return
* @throws Exception
*/
@GetMapping("class/getResourceAsStream")
public ResultData getResourceAsStream() throws Exception {
List<String> readLines = new ArrayList<>();
// 类路径下的文件时必须以"/"开头.
String classPath = "/config/配置说明.setting";
InputStream inputStream = getClass().getResourceAsStream(classPath);
if (inputStream != null) {
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
readLines = IoUtil.readLines(bufferedReader, readLines);
}
} else {
return ResultData.error("读取类路径下文件失败:" + classPath);
}
return ResultData.success(readLines);
}
Spring StreamUtils 直接读取资源内容到字符串
/**
* Spring 框架的 StreamUtils 类提供了一个便捷的方法来直接读取资源内容到字符串
* http://localhost:8080/streamUtils/copyToString
* <p>
* 即便是打包成 .jar、.war 包部署到生产环境,照样读取正常。
*
* @return
* @throws Exception
*/
@GetMapping("streamUtils/copyToString")
public ResultData streamUtils() throws Exception {
String classPath = "config/配置说明.setting";
Resource resource = new ClassPathResource(classPath);
String copyToString = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
return ResultData.success(copyToString);
}
Spring ApplicationContex 访问资源
/**
* Spring 的 ApplicationContext 提供了一种访问资源的方式,它允许你按名称查找资源。
* http://localhost:8080/applicationContext/getResource
* <p>
* 即便是打包成 .jar、.war 包部署到生产环境,照样读取正常。
* <p>
* 直接从容器中获取 ApplicationContext 实例即可,默认已经有了。
*/
@javax.annotation.Resource
private ApplicationContext applicationContext;
@GetMapping("applicationContext/getResource")
public ResultData applicationContext() throws Exception {
List<String> readLines = new ArrayList<>();
String classPath = "classpath:config/配置说明.setting";
Resource resource = applicationContext.getResource(classPath);
try (InputStream inputStream = resource.getInputStream()) {
readLines = IoUtil.readLines(inputStream, Charset.forName("UTF-8"), readLines);
}
return ResultData.success(readLines);
}
ResourceUtils 资源文件工具类
1、Spring Framework core 包下面的工具类,用于将资源位置解析为文件系统中的文件的实用程序方法。主要用于框架内部使用。
2、JAR、War 包内部的文件无法获取,必须是一个独立的文件,即项目打包后,无法再获取包中的文件。不推荐使用。
ClassPathResource 类路径资源
1、轻松读取类路径下下的任意资源文件,如果路径有前缀 / 也会自动被去掉。
@GetMapping("resource/read1")
public ResultData resource1() throws IOException {
Resource resource = new ClassPathResource("application.yml");
try (InputStream inputStream = resource.getInputStream()) {
String readUtf8 = IoUtil.readUtf8(inputStream);
return ResultData.success(readUtf8);
}
}
Hutool Setting
1、Setting 除了兼容 Properties 文件格式外,还提供了一些特有功能:各种编码方式支持、支持中文、支持${key}变量等等。
2、格式与 Properties 基本一致,文件后缀名称 可以是 .setting 或者 .properties :
Spring ResourceLoader 接口访问资源文件
1、Spring 框架提供了 Resource 接口和 ResourceLoader 接口来方便地访问资源文件。
@javax.annotation.Resource
private ResourceLoader resourceLoader;
@GetMapping("resourceLoader/read1")
public ResultData resourceLoader() throws Exception {
String classPath = "classpath:config/配置说明.setting";
Resource resource = resourceLoader.getResource(classPath);
try (InputStream inputStream = resource.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
List<String> readLines = new ArrayList<>();
readLines = IoUtil.readLines(bufferedReader, readLines);
return ResultData.success(readLines);
}
}
PathMatchingResourcePatternResolver-资源模式解析器 批量读取类路径文件资源
1、使用场景1:读取服务包中指定目录下的所有文件信息,比如想读取整个服务中的所有 .html 文件信息。
2、即便是打包成 .jar、.war 包部署到生产环境,照样读取正常。
@GetMapping("classLoader/listFiles2")
public ResultData listFiles2() throws Exception {
List<Map<String, Object>> fileInfos = new ArrayList<>();
// 创建 资源模式解析器 对象,ClassLoader 访问将通过线程上下文类加载器进行
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// 将给定的位置模式解析为 Resource 资源对象(包括目录与文件)。/* 表示直接子级(不包括孙级),/** 表示递归下面的所有子孙级资源
// 改为自己项目实际的类路径
Resource[] resources = resolver.getResources("static/**");
for (Resource resource : resources) {
Map<String, Object> fileInfo = new HashMap<>(16);
// 确定此资源的文件名,即通常是路径的最后一部分:例如“myfile.txt";如果此类资源没有文件名,则返回 null;
fileInfo.put("fileName", resource.getFilename());
// 返回此资源的描述;
fileInfo.put("description", resource.getDescription());
// 获取资源的绝对路径,并防止中文乱码
fileInfo.put("absolutePath", URLDecoder.decode(resource.getURL().getPath(), "utf-8"));
if (StrUtil.endWithIgnoreCase(resource.getFilename(), "html")) {
// 如果是 html 文件,则读取它的 title 值
String readUtf8 = IoUtil.readUtf8(resource.getInputStream());
String title = readUtf8.substring(readUtf8.indexOf("<title>") + 7, readUtf8.indexOf("</title>"));
// title=CSRF 跨站请求
// title=蚩尤后裔
System.out.println("title=" + title);
}
fileInfos.add(fileInfo);
}
return ResultData.success(fileInfos);
}