likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 15

携手创作,共同成长!这是我参与「掘金日新计划 · 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 实现类:

Spring 源码阅读 01: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 实现类可以适用当前资源的时候,可以使用它,不过,这个资源的数据流只能被读取一次。