Spring资源抽象Resource
Spring对各种底层资源,比如文件系统中的一个文件,classpath上的一个文件,或者一个网络URL,统一抽象为接口Resource来表示
因为每个底层文件都可以以一个只读InputStream的方式打开,所以Resource接口继承自接口InputStreamSource。InputStreamSource接口中定义了方法getInputStream()用于返回打开该资源所对应的输入流。
具体有如下7种实现
package com.sqz.resources;
import org.springframework.core.io.*;
import java.io.FileInputStream;
import java.io.InputStream;
public class ResourceDemo {
public static void main(String[] args) throws Exception {
{
Resource resource = new ClassPathResource("application.xml");
describe(resource);
}
{
Resource resource = new FileSystemResource("/Users/mac/workspace/spring-framework-5.2.0.RELEASE/spring-demo/src/main/java/com/sqz/RunApp.java");
describe(resource);
}
{//
Resource resource = new UrlResource("https://docs.spring.io");
describe(resource);
}
{//
Resource resource = new ByteArrayResource("Hello".getBytes());
describe(resource);
}
{// test.txt 是当前磁盘卷根目录下一个存在的文件,内容是:Test Spring Resource
InputStream is = new FileInputStream("/Users/mac/workspace/spring-framework-5.2.0.RELEASE/spring-demo/src/main/java/com/sqz/life/MyLifeStyleBean.java");
Resource resource = new InputStreamResource(is);
describe(resource);
}
}
public static void describe(Resource resource) throws Exception {
System.out.println("====================================");
System.out.println("toString : {}"+resource.toString());
System.out.println("contentLength : {}"+resource.contentLength());
System.out.println("exists : {}"+resource.exists());
System.out.println("getDescription : {}"+resource.getDescription());
System.out.println("isReadable : {}"+resource.isReadable());
System.out.println("isOpen : {}"+resource.isOpen());
System.out.println("getFilename : {}"+resource.getFilename());
System.out.println("isFile : {}"+resource.isFile());
if (resource.isFile()) {
// getFile()仅针对文件类型Resource有效,可以是文件系统文件或者classpath上的文件
System.out.println("getFile : {}"+resource.getFile());
}
if (!((resource instanceof ByteArrayResource) || (resource instanceof InputStreamResource))) {
// 以下三个属性针对 ByteArrayResource/InputStreamResource 类型资源无效,调用的话会抛出异常
System.out.println("lastModified : {}"+resource.lastModified());
System.out.println("getURI : {}"+resource.getURI());
System.out.println("getURL : {}"+resource.getURL());
}
}
}
输出结果
====================================
toString : {}class path resource [application.xml]
contentLength : {}0
exists : {}true
getDescription : {}class path resource [application.xml]
isReadable : {}true
isOpen : {}false
getFilename : {}application.xml
isFile : {}true
getFile : {}/Users/mac/workspace/spring-framework-5.2.0.RELEASE/spring-demo/build/resources/main/application.xml
lastModified : {}1672723467682
getURI : {}file:/Users/mac/workspace/spring-framework-5.2.0.RELEASE/spring-demo/build/resources/main/application.xml
getURL : {}file:/Users/mac/workspace/spring-framework-5.2.0.RELEASE/spring-demo/build/resources/main/application.xml
====================================
toString : {}file [/Users/mac/workspace/spring-framework-5.2.0.RELEASE/spring-demo/src/main/java/com/sqz/RunApp.java]
contentLength : {}394
exists : {}true
getDescription : {}file [/Users/mac/workspace/spring-framework-5.2.0.RELEASE/spring-demo/src/main/java/com/sqz/RunApp.java]
isReadable : {}true
isOpen : {}false
getFilename : {}RunApp.java
isFile : {}true
getFile : {}/Users/mac/workspace/spring-framework-5.2.0.RELEASE/spring-demo/src/main/java/com/sqz/RunApp.java
lastModified : {}1672537275111
getURI : {}file:/Users/mac/workspace/spring-framework-5.2.0.RELEASE/spring-demo/src/main/java/com/sqz/RunApp.java
getURL : {}file:/Users/mac/workspace/spring-framework-5.2.0.RELEASE/spring-demo/src/main/java/com/sqz/RunApp.java
====================================
toString : {}URL [https://docs.spring.io]
contentLength : {}-1
exists : {}true
getDescription : {}URL [https://docs.spring.io]
isReadable : {}true
isOpen : {}false
getFilename : {}
isFile : {}false
lastModified : {}0
getURI : {}https://docs.spring.io
getURL : {}https://docs.spring.io
====================================
toString : {}Byte array resource [resource loaded from byte array]
contentLength : {}5
exists : {}true
getDescription : {}Byte array resource [resource loaded from byte array]
isReadable : {}true
isOpen : {}false
getFilename : {}null
isFile : {}false
====================================
toString : {}InputStream resource [resource loaded through InputStream]
contentLength : {}498
exists : {}true
getDescription : {}InputStream resource [resource loaded through InputStream]
isReadable : {}true
isOpen : {}true
getFilename : {}null
isFile : {}false
ResourceLoader
对于每一个底层资源,比如文件系统中的一个文件,classpath上的一个文件,或者一个以URL形式表示的网络资源,Spring 统一使用 Resource 接口进行了建模抽象,相应地,对于这些资源的加载,Spring使用了 ResourceLoader 进行了统一建模抽象。
通过ResourceLoader,给定其可以接受的资源路径,我们可以获得对应资源的Resource对象,然后进行进行相应的资源访问。
Spring提供了一个缺省的ResourceLoader实现DefaultResourceLoader。该实现类可以加载classpath或者文件系统中的某个文件,或者URL形式存在的某个网络资源。Spring的各种应用上下文都间接地通过基类AbstractApplicationContext继承自DefaultResourceLoader,所以这些应用上下文自身具备并且使用DefaultResourceLoader所定义资源加载能力。
public interface ResourceLoader {
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
/**
* 给定资源路径,返回相应的资源Resource对象。所返回的Resource对象,
* 也可以叫做资源句柄,必须是可重用的资源描述符,允许多次在其上
* 调用方法Resource#getInputStream()。
* 另外 :
*
* 1. 必须支持全路径URL : 比如 "file:C:/test.dat".
* 2. 必须支持classpath伪URL : 比如 "classpath:test.dat".
* 3. 应该支持相对文件路径:比如 "WEB-INF/test.dat". (实现相关)
*
* 注意 : 所返回的资源句柄并不意味着对应的资源是已经存在的,所传入的参数
* 只是一个资源路径,并不代表相应的资源已经存在;使用者必须调用方法Resource#exists
* 来判断对应资源的存在性。
* @param location 资源路径
* @return 相应的资源句柄,总是不为null(哪怕对应的资源不存在)
*/
Resource getResource(String location);
/**
* 暴露当前ResourceLoader所使用的ClassLoader给外部。
*/
@Nullable
ClassLoader getClassLoader();
}
测试ResourceLoader
package com.sqz.resources;
import org.springframework.core.io.*;
public class SpringDefaultResourceLoaderDemo {
public static void main(String[] args) throws Exception {
load();
}
public static void load() throws Exception {
ResourceLoader resourceLoader = new DefaultResourceLoader();
{// 获取 classpath 上的某个资源 : 路径带前缀 classpath:
Resource resource = resourceLoader.getResource(
"classpath:application.xml");
describe(resource);
}
{// 获取 classpath 上的某个资源 : 路径不带前缀 classpath:
Resource resource = resourceLoader.getResource(
"application.xml");
describe(resource);
}
{// 获取 classpath 上的某个资源 : 路径前缀为/,而不是 classpath:
Resource resource = resourceLoader.getResource(
"/application.xml");
describe(resource);
}
{// 获取网络上指定 url 的某个资源
Resource resource = resourceLoader.getResource(
"https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference"
+"/pdf/spring-framework-reference.pdf");
describe(resource);
}
{// 获取文件系统中的某个资源
// test.txt 是当前磁盘卷根目录下一个存在的文件,内容是:Test Spring Resource
Resource resource = resourceLoader.getResource("file:/Users/mac/workspace/spring-framework-5.2.0.RELEASE/spring-demo/src/main/java/com/sqz/RunApp.java");
describe(resource);
}
}
public static void describe(Resource resource) throws Exception {
System.out.println("====================================");
System.out.println("toString : {}"+resource.toString());
System.out.println("contentLength : {}"+resource.contentLength());
System.out.println("exists : {}"+resource.exists());
System.out.println("getDescription : {}"+resource.getDescription());
System.out.println("isReadable : {}"+resource.isReadable());
System.out.println("isOpen : {}"+resource.isOpen());
System.out.println("getFilename : {}"+resource.getFilename());
System.out.println("isFile : {}"+resource.isFile());
if (resource.isFile()) {
// getFile()仅针对文件类型Resource有效,可以是文件系统文件或者classpath上的文件
System.out.println("getFile : {}"+resource.getFile());
}
if (!((resource instanceof ByteArrayResource) || (resource instanceof InputStreamResource))) {
// 以下三个属性针对 ByteArrayResource/InputStreamResource 类型资源无效,调用的话会抛出异常
System.out.println("lastModified : {}"+resource.lastModified());
System.out.println("getURI : {}"+resource.getURI());
System.out.println("getURL : {}"+resource.getURL());
}
}
}
用户为DefaultResourceLoader提供自定义的ProtocolResolver用来识别特定格式的资源路径
使用方法DefaultResourceLoader#addProtocolResolver添加ProtocolResolver,可以提供多个,也可以不提供。
缺省情况下,DefaultResourceLoader不包含ProtocolResolver。
给定一个路径,调用方法DefaultResourceLoader#getResource获取资源对象(也可以叫做句柄)
1 轮询每个ProtocolResolver看它们哪个可以处理该路径,如果可以让其处理并返回相应的资源句柄;
2 如果路径以"/"开头,尝试将其处理为一个ClassPathContextResource句柄并返回;
3 如果路径以"classpath:“前缀开头,去除路径中前缀部分之后将其封装成一个ClassPathResource句柄并返回;
4 如果路径是URL格式
5 如果路径以"file:”/“vfs:”/"vfsfile:"前缀开头,将其封装成一个FileUrlResource句柄并返回;
6 否则将其封装成一个UrlResource句柄返回;
7 其他格式,仍然尝试将其封装一个ClassPathContextResource句柄并返回;