Java 的标准 java.net.URL
类和各种 URL
前缀的标准处理程序不足以满足所有对低级资源的访问。 例如没有标准化的 URL
实现可用于访问需要从类路径或相对于 ServletContext
获取的资源。 虽然可以为专门的 URL 前缀注册新的处理程序(类似于现有的前缀处理程序如 http:
),但这通常相当复杂,并且 URL 接口仍然缺乏一些理想的功能,例如检查是否存在的方法 指向的资源。
针对上述这种情况,Spring 提供了更强大的接口 Resource
用于对低级资源的抽象访问,其定义和主要接口方法说明如下。
package org.springframework.core.io;
public interface Resource extends InputStreamSource {
// 特定资源是否存在
boolean exists();
// 内容非空可通过#getInputStream()读取
default boolean isReadable() {
return exists();
}
// 资源对应的InputStream是否已经打开,不能多次读取,应读取后关闭避免资源泄漏
default boolean isOpen() {
return false;
}
// 是否是 File 类型,配合 #getFile()
default boolean isFile() {
return false;
}
// 获取 URL
URL getURL() throws IOException;
// 获取URI
URI getURI() throws IOException;
// 获取 File
File getFile() throws IOException;
// 资源内容长度
long contentLength() throws IOException;
// 上次修改的时间戳
long lastModified() throws IOException;
// 给定路径创建资源
Resource createRelative(String relativePath) throws IOException;
// 获取文件名非全路径
@Nullable
String getFilename();
// 获取资源描述
String getDescription();
}
Resource
接口继承了 InputStreamSource
,其定义如下。
package org.springframework.core.io;
public interface InputStreamSource {
// 获取资源对应的 InputStream
InputStream getInputStream() throws IOException;
}
Resource
接口并不是用于完全取代 java.net.URL
,而是尽可能地通过其实现类来包装 URL
进行处理,如 UrlResource
包装一个 URL
并使用包装的 URL
来完成它的的功能。具体看下一节的内置实现。
UrlResource
包装了 java.net.URL
,可用于访问通常可通过 URL
访问的任何对象,例如文件、HTTP 目标、FTP 目标等。这些资源通过标准前缀来区分, file:
用于访问文件系统路径, http:
用于通过 HTTP 协议访问资源, ftp:
用于通过 FTP 访问资源等。
ClassPathResource
表示应从类路径获取的资源。它使用线程上下文类加载器、给定的类加载器或给定的类来加载资源。可通过特定前缀 classpath:
指定为该资源。
如果类路径资源驻留在文件系统中,则此 Resource
实现支持解析为 java.io.File
,但不支持在 jar 包中且尚未解压的类路径资源。
这是 java.io.File
和 java.nio.file.Path
句柄的资源实现。它支持作为 File
和 URL
的解析。
这是 ServletContext
资源的 Resource
实现,它解释相关 Web 应用程序根目录中的相对路径。
它始终支持流访问和 URL
访问,但仅在扩展 Web 应用程序存档并且资源物理位于文件系统上时才允许 java.io.File
访问。它是否被解压并在文件系统上或直接从 JAR 或其他地方(如数据库)访问实际上取决于 Servlet 容器。
给定字节数组的资源实现。它为给定的字节数组创建一个 ByteArrayInputStream
。对于从任何给定的字节数组加载内容很有用,而不必求助于单一使用的 InputStreamResource
。
InputStreamResource
是给定 InputStream
的资源实现。首选 ByteArrayResource
或任何基于文件的 Resource
实现,仅当没有特定的 Resource
实现适用时才应使用它。
ResourceLoader 用于加载资源(例如类路径或文件系统资源)的策略接口。
package org.springframework.core.io;
public interface ResourceLoader {
/** Pseudo URL prefix for loading from the class path: "classpath:". */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
// 返回指定资源位置的资源句柄
Resource getResource(String location);
// 获取 ClassLoader
// 需要直接访问 ClassLoader 的客户端可以通过 ResourceLoader 统一的方式进行访问,而不是依赖于线程上 下文 ClassLoader。
@Nullable
ClassLoader getClassLoader();
}
源码中对于的说明:
- 句柄应始终是可重用的资源描述符,允许多个 Resource.getInputStream() 调用。
- 必须支持完全限定的 URL,例如”
file:C:/test.dat
“。 - 必须支持类路径伪 URL,例如”
classpath:test.dat
“。 - 应该支持相对文件路径,例如”
WEB-INF/test.dat
“。 (这将是特定于实现的,通常由ApplicationContext
实现提供。) - 资源句柄并不意味着现有资源;需要调用
Resource.exists()
来检查是否存在。
所有 org.springframework.context.ApplicationContext
都必须实现该 ResourceLoader
接口。调用 getResource()
接口的返回值类型取决于当前context的类型,当前context会自动转换。如下案例。
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
如 ctx
是 ClassPathXmlApplicationContext
返回 ClassPathResource
;
如 ctx
是 FileSystemXmlApplicationContext
返回 FileSystemResource
;
如
ctx 是WebApplicationContext返回
ServletContextResource
如果不想依赖 context
类型决定返回资源的类型,可以指定前缀的方式,强制返回特定类型的资源。如下案例。
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
// 返回 ClassPathResource
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
// 返回 UrlResource
指定前缀对于资源加载方式和返回的影响对应关系如下表。
前缀 例子 如何解析 classpath:
从类路径加载 file: file:///data/config.xml 从文件系统作为</p>
<p>加载 http:</p>
<p>从文件系统作为</p>
<p>加载 (none)</p>
<p>取决于底层的</p>
<p><code>ResourceLoaderAware</code> 接口是一个特殊的回调接口,它标识期望提供 <code>ResourceLoader</code> 引用的组件。其定义如下。</p>
<pre><code class="language-java">package org.springframework.context;
public interface ResourceLoaderAware extends Aware {
// 可自定义保存一个ResourceLoader的引用来进行资源加载
void setResourceLoader(ResourceLoader resourceLoader);
}
</code></pre>
<p>由于 <code>org.springframework.context.ApplicationContext</code>实现了 <code>ResourceLoader</code>,因此 bean 还可以实现 <code>ApplicationContextAware</code> 接口并直接使用提供的应用程序上下文来加载资源。从面向接口编程和解耦的角度来说,需要 <code>ResourceLoader</code>的来进行资源加载,更推荐实现
ResourceLoaderAware 接口。</p>
<p>深一层思考,如果容器中的一个bean需要一个 <code>ResourceLoader</code>依赖用于加载资源,除了实现 <code>ResourceLoaderAware</code>,是否还有其它方式呢?</p>
<p><code>ResourceLoader</code>实例会自动注入到IoC容器,我们可通过构造函数、setter方法、@Autowire注解等方式,直接注入该依赖,具体看一看之前Ioc相关的文章。</p>
<p>如果某个bean依赖于特定的 <code>Resource</code>,那么我们如何快速优雅地注入该依赖呢?直接上代码。</p>
<pre><code class="language-java">public class MyConfig {
/**
* 通过配置文件注入的资源
*/
private Resource template;
public Resource getTemplate() {
return template;
}
public void setTemplate(Resource template) {
this.template = templa
}
}
</code></pre>
<p><code>MyConfig</code>依赖一个类文件路径下的一个配置文件。</p>
<p>对应的xml文件 <code>spring.xml</code>和配置文 <code>db.properties</code>件如下:</p>
<pre><code class="language-xml">
</code></pre>
<pre><code class="language-properties">url=jdbc:mysql://localhost/test
username=root
password=456
max=1024
</code></pre>
<p>运行主程序如下:</p>
<pre><code class="language-java">package com.crab.spring.ioc.demo02;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
public class ResourceTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring" +
".xml");
MyConfig myConfig = context.getBean(MyConfig.class);
Resource template = myConfig.getTemplate();
System.out.println(template);
System.out.println(template instanceof ClassPathResource);
}
}
</code></pre>
<p>运行结果:</p>
<pre><code class="language-java">class path resource [db.properties]
true
class path resource [db.properties]
</code></pre>
<ul>
<li>成功注入了 <code>Resourece</code>资源到 <code>MyConfig</code>的bean中</li>
<li>注入 <code>Resource</code>实际是我们通过 <code>ClassPathXmlApplicationContext</code>加载的 <code>ClassPathResource</code></li>
</ul>
<p>扩展:结合上一篇的资源特定前缀和 <code>ApplicationContext</code>的关系,忘了请翻看下,我们也可以指定前缀来加载特定的资源。较为简单就不演示了。</p>
<pre><code class="language-xml">
file:///some/resource/path
</code></pre>
<pre><code class="language-xml">
// 注入的是FileSystemResource
</code></pre>
<p>回顾一下, <code>ResourceLoader</code>章节,我们分析了指定资源路径前缀对于资源加载方式的影响,对应关系如下表。下面将分带前缀和不带前缀的方式来配置应用上下文。</p>
<p>前缀 例子 如何解析 classpath:</p>
<p>从类路径加载 file:
:///data/config.xml 从文件系统作为
加载 http:
从文件系统作为
加载 (none)
取决于底层的
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
上面这个案例资源路径不带前缀, ClassPathXmlApplicationContext
将使用 ClassPathResource
加载在类路径下的资源文件。再来一个案例。
ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");
FileSystemXmlApplicationContext
将使用 URLResouce
来加载文件系统中的配置文件,这里案例是相对路径。
当指定了资源前缀,则使用指定前缀对应的 Resource
来加载资源。如下案例。
ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
对比上一个案例,此处将使用 ClassPathResource
加载在类路径下的资源文件。
指定单一的资源文件通过前面2种方式,若需要指定多个资源则可以考虑使用通配符,支持 Ant-style
和 classpath*
。
出于向后兼容的历史原因, FileSystemXmlApplicationContext
关联的 FileSystemResource
会将所有的资源的非前缀路径统一当做相对路径。上案例
// 2种写法是一样的
ApplicationContext ctx =new FileSystemXmlApplicationContext("conf/context.xml");
ApplicationContext ctx =new FileSystemXmlApplicationContext("/conf/context.xml");
// 2种写法是一样的
ctx.getResource("some/resource/path/myTemplate.txt");
ctx.getResource("/some/resource/path/myTemplate.txt");
// 可以强制不按相对路径处理不,可以的!按URLResource处理
ctx = new FileSystemXmlApplicationContext("file:///conf/context.xml");
本文主要详细说明了
这一篇下来,基本Spring关于资源处理和相关接口总体是比较清晰了。
知识分享,转载请注明出处。学无先后,达者为先!
Original: https://www.cnblogs.com/kongbubihai/p/15915434.html
Author: kongxubihai
Title: Spring系列18:Resource接口及内置实现
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/572046/
转载文章受原作者版权保护。转载请注明原作者出处!