1 资源抽象接口(Resource)
JDK 所提供的访问资源的类(如 java.net.URL、File 等)并不能很好的满足各种底层资源的访问需求,比如缺少从类路径或者 Web 容器的上下文中获取资源的操作类 。 为此, Spring 设计了一个 Resource 接口,它为应用程序提供了更强的访问底层资源的能力 。
Resource 接口的主要方法:
方法 | 说明 |
---|---|
boolean exists() | 是否存在 |
boolean isOpen() | 是否打开 |
URL getURL() throws IOException | 如果底层资源可以表示成 URL ,则返回对应的 URL 对象 |
File getFile() throws IOException | 如果底层资源对应一个文件,则返回对应的 File 对象 |
InputStream getInputStream() throws IOException | 返回资源对应的输入流 |
接口或类 | 说明 |
---|---|
WritableResource | 可写资源接口(Spring 3.1 + 新增),有两个实现类:FileSystemResource 和 PathResource(Spring 4.0 + 新增) |
ByteArrayResource | 二进制数组表示的资源,二进制数组资源可以在内存中通过程序构造。 |
ClassPathResource | 类路径下的资源,资源以相对于类路径的方式表示。 |
FileSystemResource | 文件系统资源,资源以文件系统路径的方式表示,如 D:/config.xml。 |
InputStreamResource | 以输入流返回表示的资源。 |
ServletContextResource | 以相对于 Web 应用根目录的路径下加载资源。支持以流和 URL 的方式访问资源;在 WAR 被解压的情况下,也可以通过 File 方式访问资源;还可以直接从 JAR 包中访问资源。 |
UrlResource | Url 封装了 ava.net.URL,它能够访问如文件系统的资源、HTTP 以及 FTP 等资源。 |
PathResource | Path 封装了 java.net.URL、java.nio.file.Path(Java 7.0 +)和文件系统资源,通过它可以访问 URL、Path 和系统文件路径表示的资源。 |
有了这个抽象的资源类之后,就可以将 Spring 的配置信息放置在任意地方咯O(∩_∩)O哈哈~
Spring 的 Resource 接口及其实现类,可以在脱离 Spring 框架的情况下实现,它与 JDK 提供的资源访问方式相比,更强大,也更好用。
假设需要访问 Web 应用类路径下的一个文件,那么我们可以使用以下这些方法:
- FileSystemResource - 以文件系统的绝对路径的方式进行访问。
- ClassPathResource - 以类路径的方式进行访问。
- ServletContextResource - 以相对于 Web 应用根目录的路径的方式进行访问。
public class FileSourceTest {
public static void main(String[] args) throws IOException {
String filePath="F:\\temp\\config.xml";
//以系统文件路径的方式加载资源
WritableResource writableResource=new PathResource(filePath);
//写入
OutputStream outputStream=writableResource.getOutputStream();
outputStream.write("今天过得还好吗?".getBytes());
outputStream.close();
//读取
InputStream inputStream=writableResource.getInputStream();
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
int i;
while ((i=inputStream.read())!=-1){
byteArrayOutputStream.write(i);
}
System.out.println("byteArrayOutputStream:"+byteArrayOutputStream.toString());
System.out.println("writableResource-getFilename:"+writableResource.getFilename());
//使用类路径的方式加载资源
Resource resource=new ClassPathResource("resources/spring-ioc.xml");
System.out.println("resource-getFilename:"+resource.getFilename());
}
}
Resource 接口定义一些方法,用于访问文件的信息与数据:
方法 | 说明 |
---|---|
getFileName() | 获取文件名 |
getFile() | 获取资源对应的 File 对象 |
getInputStream() | 获取文件的输入流 |
WritableResource 接口定义一些方法,用于向文件写入数据:
方法 | 说明 |
---|---|
getOutputStream() | 获取文件的输出流 |
createRelative(String relativePath) | 在相对地址上创建新文件 |
在 Web 应用中,可以通过 ServletContextResource 以相对于 Web 应用根目录的路径,加载文件资源:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<jsp:directive.page import="org.springframework.web.context.support.ServletContextResource"/>
<jsp:directive.page import="org.springframework.core.io.Resource"/>
<jsp:directive.page import="org.springframework.web.util.WebUtils"/>
<%
Resource resource=new
ServletContextResource(application,"WEB-INF/resources/config.properties");//相对于 Web 应用的根路径
out.print(resource.getFilename()+"<br/>");
out.print(WebUtils.getTempDir(application).getAbsoluteFile());
%>
对于位于远程服务器(Web 服务器或 FTP 服务器)上的文件资源,可以使用 UrlResource 进行访问。
加载资源默认采用系统编码来读取内容。所以如果资源文件的内容采用的是特殊的编码格式,那么可以使用 EncodedResource 对资源进行编码:
EncodedResource encodedResource=new EncodedResource(resource,"UTF-8");
System.out.println(FileCopyUtils.copyToString(encodedResource.getReader()));
2 通过特殊标识加载资源
Spring 提供了一个强大的加载资源的机制,可以通过 "classpath:"、"file:" 等资源地址前缀识别不同的资源类型,还支持 Ant 风格的带通配符的资源地址 。
2.1 资源地址表达式
地址前缀 | 说明 | 示例 |
---|---|---|
classpath: | 从类路径中加载资源, classpath: 和 classpath:/ 是等价的,都是相对于类的根路径 。 资源文件可以在标准文件系统中,也可以在 jar 或者 zip 文件中 。 | classpath:com/xxx/config.properties |
file: | 使用 UrlResource 从文件系统目录中装载资源,可以采用绝对或者相对路径 。 | file:com/xxx/config.properties |
http:// | 使用 UrlResource 从 Web 服务器中装载资源。 | http://www.xxx.com/xxx/config.properties |
ftp:// | 使用 UrlResource 从 FTP 服务器中装载资源。 | ftp://www.xxx.com/xxx/config.properties |
没有前缀 | 根据 ApplicationContext 具体实现类采用对应类型的 Resource。 | com/xxx/config.properties |
"classpath*:" 前缀与 “classpath:” 前缀的区别如下:
假设有这样的一个场景,假设有在 JAR 包或文件系统类路径下,存在多个同名的包名(比如 com.deniro)——
- "classpath*:" 前缀 - 会扫描所有这些 JAR 包或文件系统类路径下的同名包。
- “classpath:” 前缀 - 只会扫描这些 JAR 包或文件系统类路径下的第一个被加载的包。
这种设计对于分模块打包的应用场景下很有用。假设一个名为 note 的应用,分为 2 个模块,每个模块对应一个配置文件(module1.xml 与 module2.xml ),都放置在 com.note 目录下,每个模块单独打 JAR 包。使用 classpath*:com/note/module*.xml
就可以同时加载这些模块中的配置文件啦O(∩_∩)O哈哈~
Ant 风格的资源地址支持以下 3 种匹配符:
匹配符 | 说明 |
---|---|
? | 匹配文件名中的一个字符。 |
* | 匹配文件名中的任意个字符 。 |
** | 匹配多层路径。 |
2.2 资源加载器
Spring 定义了加载资源的接口以及实现类:
接口或类 | 说明 |
---|---|
ResourceLoader | getResource(String location) 方法,可以根据地址加载资源,仅支持带资源类型前缀的表达式。 |
ResourcePatternResolver | 扩展了 ResourceLoader 接口,定义的 getResources(String locationPattern) 方法,支持带资源类型前缀及 Ant 风格的资源路径表达式。 |
PathMatchingResourcePatternResolver | 是 ResourcePatternResolver 的实现类。 |
public void getResources() throws IOException {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath*:net/deniro/**/*.class");
Assert.assertNotNull(resources);
for (Resource resource : resources) {
System.out.println(resource.getDescription());
}
}
在项目发布时,如果资源配置文件会被打包到 JAR 中,如果使用 Resource#getFile() 方法,会抛出 FileNotFoundException,我们可以使用 Resource#getInputStream() 来读取它。建议在实践中,尽量采用流的方式来读取配置文件,因为它总是有效的哦 O(∩_∩)O哈哈~
网友评论