Spring 源码阅读 01:Resource 资源抽象

语言: CN / TW / HK

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情

在 Spring 的核心源码中,Resource 接口定义了 Spring 对底层资源访问的抽象,通过实现 Resource 接口,我们可以开发各种资源的访问能力。以 Spring 自身为例,Resource 的使用非常广泛,我们可以在源码中的很多方法或者构造方法中看到 Resource 类型的参数。比如,通过 XML 加载容器配置、从 application.properties 文件读取 Spring Boot 项目配置信息,都是通过 Resource 接口的实现类来实现对这些文件的访问的。

Resource 接口

Resource 接口的定义如下:

public interface Resource extends InputStreamSource { boolean exists(); boolean isReadable(); boolean isOpen(); boolean isFile(); URL getURL() throws IOException; URI getURI() throws IOException; File getFile() throws IOException; ReadableByteChannel readableChannel() throws IOException; long contentLength() throws IOException; long lastModified() throws IOException; Resource createRelative(String relativePath) throws IOException; String getFilename(); String getDescription(); }

在以上的方法中:

  • exist()isXXX() 方法提供了对资源状态的判断;
  • getFile()getURI()getURL() 提供了资源到FileURIURL的转换;
  • getFilename()getDescription() 提供了一些资源信息的获取,一般用于日志信息。

此外,它还继承了 InputStreamSource 接口:

public interface InputStreamSource { InputStream getInputStream() throws IOException; }

这里的 getInputStream() 提供了从资源获取输入流的方法,每次调用这个方法,都需要返回一个新的输入流,不过要注意,输入流不在被使用的时候,一定要记得关闭。

Resource 实现类

在 Spring 中,已经包含了很多 Resource 的实现类,如下是一些常见的 Resource 实现类:

从这些实现类的名称中,我们就能看出它们分别实现了那些来源的资源访问。下面我们分别分析一下这几个实现类:

FileSystemResource

一个 FileSystemResource 可以通过 File 对象或者文件路径来创建,在 FileSystemResource 类中,有以下三个成员变量:

``` private final String path;

@Nullable private final File file;

private final Path filePath; ```

它们会在构造方法中被初始化,值分别是文件路径字符串、访问文件的 File 对象、文件路径对应的 Path 对象。

当调用 getInputStream() 方法的时候,会通过 File 对象创建一个 InputStream 对象。其他的一些操作也都会在 File 对象上完成。

ByteArrayResource

ByteArrayResource 的名字就可以看出,创建它并不需要外部资源,只需要提供一个 ByteArray 即可。在 ByteArrayResource 中只有两个成员变量:

``` private final byte[] byteArray;

private final String description; ```

分别代表 byteArray 本身和其描述信息,我们可以在创建对象时给它提供一个描述,如果不提供,默认的描述信息是 resource loaded from byte array,当需要调用 getInputStream() 获取输入流的时候,将会得到一个 ByteArrayInputStream。实现方法如下:

@Override public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(this.byteArray); }

ByteArrayResource 比较适用于一些需要用 ByteArray 创建一次性输入流的地方。

UrlResource

UrlResource 可以使用 URL 创建,也可以使用 URI 创建,可以用来访问一些需要通过 HTTP 或者 FTP 等访问的文件和内容。

当调用 getInputStream() 方法获取数据流的时候,会通过 URL 创建 URLConnection ,然后从中获取 InputStream 对象,代码如下:

@Override public InputStream getInputStream() throws IOException { URLConnection con = this.url.openConnection(); ResourceUtils.useCachesIfNecessary(con); try { return con.getInputStream(); } catch (IOException ex) { // Close the HTTP connection (if applicable). if (con instanceof HttpURLConnection) { ((HttpURLConnection) con).disconnect(); } throw ex; } }

ClassPathResource

ClassPathResource 提供了从类路径访问资源的方法,创建 ClassPathResource 需要提供一个资源路径,以及一个类加载器或者一个类对象。我们通常在 Spring 的配置文件中使用 classpath: 作为前缀的资源路径,都是通过 ClassPathResource 读取的。

InputStreamResource

我们也可以使用 InputStreamResource 将一个已经创建的 InputStream 封装成一个 Resource,当通过 getInputStream() 获取输入流的时候,会得到我们创建 InputStreamResource 时提供的 InputStream。当没有其他的 Resource 实现类可以适用当前资源的时候,可以使用它,不过,这个资源的数据流只能被读取一次。