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 實現類可以適用當前資源的時候,可以使用它,不過,這個資源的資料流只能被讀取一次。