Spring Resource资源文件体系

1. Resource接口

Spring对于资源加载有着一套自己的框架——Resource,Resource继承自InputStream。
下面的是Resource的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public interface Resource extends InputStreamSource {
boolean exists();//判断资源是否存在
default boolean isReadable() {
return true;
}
//判断资源是否打开
default boolean isOpen() {
return false;
}
//判断资源是否是一个文件
default boolean isFile() {
return false;
}
//获取资源文件的URL
URL getURL() throws IOException;
//获取资源文件的URI
URI getURI() throws IOException;
//获取资源文件的File对象
File getFile() throws IOException;
///这个方法接口中有默认实现,返回的是ReadableByteChannel,这个类属于Java的NIO中的管道。
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
//获取内容的长度,这个方法返回一个long,因为文件内容可能很长。
long contentLength() throws IOException;
//这个方法返回的是最后修改时间,虽然也返回的是long,但是这个数字是一个时间戳。
long lastModified() throws IOException;
//这个方法根据relativePath相对路径返回一个相对与该Resource的Resource。
Resource createRelative(String relativePath) throws IOException;
@Nullable
//获取文件的名字。
String getFilename();
//获取一个对该资源的一个描述。
String getDescription();

}

2. Resource实现类

你可以理解为,Resource就是一个增强版的InputStreamSource,Resource 接口是Spring资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略(策略模式)。

  • UrlResource:
    • UrlResource封装了java.net.URL,可用于访问通常可通过url访问的任何对象,如文件、HTTP目标、FTP目标和其他对象。所有URL可以使用一个标准化前缀来表示一个URL类型。例如: file:用于访问文件系统路径。 http:用于通过HTTP协议访问资源。 ftp:用于通过FTP访问资源。
  • ClassPathResource:
    • 表示从类路径加载资源。如果资源路径带上前缀ClassPath:,那么会隐式的解析为ClassPathResource。注意,如果类资源文件是在文件系统中,则该资源实现会被解析为java.io.File, 如果是在Jar包中,则会使用java.net.URL来解析。
  • FileSystemResource:
    • 他是java.io.File和java.nio.file.Path的Resource实现,支持解析为File或者URL。如D:/aaa/vvv.java
  • ServletContextResource:
    • 这是ServletContext的Resource实现,用于解释相关Web应用程序根目录中的相对路径。访问Web容器上下文中的资源而设计的类,负责对于Web应用根目录的路径加载资源。它支持以流和URL的方式访问,在WAR解包的情况下,也可以通过File方式访问。该类还可以直接从JAR包中访问资源。
  • InputStreamResource:
    • InputStreamResource 是InputStream 的Resource实现。只有在其他Resource实现不可用的时候才考虑使用它。和其他的Resource实现相反,它是一个already-opened resource的描述器,所以isOpen()会返回true。 如果你想保存资源描述器或者多次读取一个stream, 那么不要使用它。
  • ByteArrayResource:
    • 是byte array的Resource实现, 它创建了ByteArrayInputStream。它对于从任何给定的字节数组加载内容都很有用,而不必求助于单次使用的InputStreamResource。
  • PathResource:
    • Spring4.0提供的读取资源文件的新类。Path封装了java.net.URL、java.nio.Path、文件系统资源,它使用户能够访问任何可以通过URL、Path、系统文件路径表示的资源,如文件系统的资源,HTTP资源、FTP资源等。

3. 使用示例

使用示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test
public void testResource() throws IOException {
String filePath = "E:\\源码\\Spring源码阅读\\testSpring\\src\\test\\resources\\spring.txt";

// 1. 使用系统的文件路径方式加载文件
WritableResource resource1 = new PathResource(filePath);

// 2. 使用类路径方式加载文件
Resource resource2 = new ClassPathResource("spring.txt");

// 3. 使用WritableResource接口写资源文件
OutputStream os = resource1.getOutputStream();
os.write("Spring是一套非常优秀的框架".getBytes());

// 4. 使用Resource接口读文件
InputStream in1 = resource1.getInputStream();
InputStream in2 = resource1.getInputStream();
BufferedInputStream bis = new BufferedInputStream(in1);
byte[] bytes = new byte[1024];
bis.read(bytes);
System.out.println(new String(bytes));
System.out.println("resource1: " + resource1.getFilename());
System.out.println("resource2: " + resource2.getFilename());
}

4. 资源路径通配符。

Resource解析各种资源路径,依靠资源路径通配符可以带来很多方便。

4.1 Ant-style Patterns

定义资源路径可以是用Ant风格的通配符,下面是 Ant-style patterns 的路径例子:

1
2
3
4
/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-cont?xt.xml
classpath:com/mycompany/**/applicationContext.xml

Ant风格的资源地址支持三种通配符:

  • ?:匹配文件名中的一个字符
  • *:匹配文件名中的多个字符
  • **:匹配多层路径。

4.2 classpath*:前缀

构造基于XML的ApplicationContext,路径地址可以使用classpath*: 前缀,如下:

1
2
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");

或者

1
2
3
4
5
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xmlclasspath*:applicationContext-shiro.xml
</param-value>
</context-param>

classpath* 和 classpath 的区别是:classpath* 会去查找所有匹配的classpath, 而classpath 只会找到第一个匹配的资源。

5. ResourceLoader

不过有个问题随之而来,那就是Resource的选择,这么多的Resource如何知道选择使用哪一个?Spring提供了一个强大的资源加载机制,他可以通过前缀标识加载资源,如:classpath:, file:,ftp:等,同时还支持使用Ant风格的通配符。

ResourceLoader用来返回Resource实例,下面是其定义:

1
2
3
public interface ResourceLoader {
Resource getResource(String location);
}
前缀 例子 说明
classpath: classpath:com/myapp/config.xml 使用ClassPathResource从classpath中加载。
file: file:/data/config.xml 作为 URL 加载。使用UrlResource从文件系统目录中装载资源
http: http://myserver/logo.png 作为 URL 加载。使用UrlResource从Web服务器中装载资源
ftp: ftp://www.mcwebsite.top/bean.xml 作为 URL 加载。使用UrlResource从ftp服务器中装载资源
(none) /data/config.xml 根据ApplicationContext的具体实现选择对应类型的Resource

上表中最后一种情况,需要说明下:

所有的ApplicationContext都实现了ResourceLoader类。因此所有的ApplicationContext都可以用来获取Resource。

当在特定的应用程序上下文上调用getResource(),并且指定的位置路径没有特定的前缀时,将返回适合该特定应用程序上下文的资源类型。

例如,假设对ClassPathXmlApplicationContext实例执行了以下代码片段:

Resource template = ctx.getResource("some/resource/path/myTemplate.txt");

在ClassPathXmlApplicationContext中,这个方法返回ClassPathResource。

以此类推,在FileSystemXmlApplicationContext中,方法返回FileSystemResource。在WebApplicationContext, 方法返回ServletContextResource。

当然,就像我们表中说的,我们可以强制使用ClassPathResource,而不管ApplicationContext到底是什么。这样做的话,我们需要添加classpath:前缀。如下:

Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");

1
2
3
4
5
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xmlclasspath*:applicationContext-shiro.xml
</param-value>
</context-param>

同样的,你可以强制使用UrlResource通过添加标准的java.net.URL前缀(context-param配置的话同理):

1
2
3
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");

Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
0%