Spring FrameworkのResourceインターフェースのおさらいメモ

# 概要

Spring FrameworkのResourceインターフェースと実装クラスをを使ったリソースファイルのアクセス方法をおさらいしたときのメモです。

**環境**

* Windows 10 Professional 1909
* OpenJDK 13
* Spring Boot 2.2.1

**参考**

* [Core Technologies - 2. Resources] (https://docs.spring.io/spring/docs/5.2.1.RELEASE/spring-framework-reference/core.html#resources)
* [Package org.springframework.core.io] (https://docs.spring.io/spring/docs/5.2.1.RELEASE/javadoc-api/)


# [Resource] (https://docs.spring.io/spring/docs/5.2.1.RELEASE/javadoc-api/org/springframework/core/io/Resource.html)

**Since 28.12.2003**

> Interface for a resource descriptor that abstracts from the actual type of underlying resource, such as a file or class path resource.

Resourceインターフェースのインスタンスを取得する方法には

1. 実装クラスを使用する
1. `ResourceLoader`を使用する
1. `@Value`アノテーションを使用する

などがあります。

## 実装クラスを使用する方法

リソースファイルのロケーションに応じて適切なResourceインタフェースの実装クラスを使用する方法です。
たとえば、クラスパス内のリソースにアクセスするための`ClassPathResource`、ネットワーク上のリソースにアクセスするための`UrlResource`、ファイルシステム上のリソースにアクセスするための`FileUrlResource`があります。

### [ClassPathResource] (https://docs.spring.io/spring/docs/5.2.1.RELEASE/javadoc-api/org/springframework/core/io/ClassPathResource.html)

**Since 28.12.2003**

> Resource implementation for class path resources. Uses either a given ClassLoader or a given Class for loading resources.

|コンストラクタ|
|:------------|
|ClassPathResource(String path) |
|ClassPathResource(String path, Class<?> clazz) |
|	ClassPathResource(String path, ClassLoader classLoader) |

**コード**

```java
Resource resource = new ClassPathResource("config/app.properties");
```

Resourceインタフェースは`InputStreamSource`インタフェースを継承しているのでgetInputStreamメソッドで、リソースファイルの`InputStream`を取得することができます。

```java
String read(Resource resource) {
    try (InputStream in = resource.getInputStream();
         BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
        StringBuilder sb = new StringBuilder();
        String line = null;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        return sb.toString();
    } catch (IOException e) {
        throw new UncheckedIOException(e);
    }
}
```

なお、ResourceインタフェースはgetFileメソッドもあるので、以下のように記述することができますが、リソースがクラスパス内やネットワーク上にある場合は例外をスローするので、通常はリソースファイルがファイルシステム上にある場合に限って使った方がいいと思います。

```java
String read(Resource resource) {
    try {
        return Files.readString(resource.getFile().toPath());
    } catch (IOException e) {
        throw new UncheckedIOException(e);
    }
}
```

### [UrlResource] (https://docs.spring.io/spring/docs/5.2.1.RELEASE/javadoc-api/org/springframework/core/io/UrlResource.html)

**Since 28.12.2003**

> Resource implementation for java.net.URL locators. Supports resolution as a URL and also as a File in case of the "file:" protocol.

|コンストラクタ|
|:------------|
|UrlResource(String path) |
|UrlResource(String protocol, String location) |
|UrlResource(String protocol, String location, String fragment) |
|UrlResource(java.net.URI uri) |
|UrlResource(java.net.URL url) |

**コード**

```java
Resource urlResource = new UrlResource("http://localhost:8887/config/app.properties");
```

### [FileUrlResource] (https://docs.spring.io/spring/docs/5.2.1.RELEASE/javadoc-api/org/springframework/core/io/FileUrlResource.html)

**Since 5.0.2**

> Subclass of UrlResource which assumes file resolution, to the degree of implementing the WritableResource interface for it. This resource variant also caches resolved File handles from getFile().

|コンストラクタ|
|:------------|
|FileUrlResource(String location) |
|FileUrlResource(java.net.URL url) |

**コード**

```java
Resource fileUrlResource = new FileUrlResource("c://var//config/app.properties");
```

## ResourceLoaderを使用する方法

**Since 10.03.2004**

> Strategy interface for loading resources (e.. class path or file system resources). An ApplicationContext is required to provide this functionality, plus extended ResourcePatternResolver support.
DefaultResourceLoader is a standalone implementation that is usable outside an ApplicationContext, also used by ResourceEditor.

[ResourceLoader] (https://docs.spring.io/spring/docs/5.2.1.RELEASE/javadoc-api/org/springframework/core/io/ResourceLoader.html)インタフェースの実装クラスを使ってResourceのインスタンスを取得すれば、Resourceインタフェースの実装クラスを意識せずにすむというメリットがあります。

Spring Bootでは、ResourceLoaderには`AnnotationConfigServletWebServerApplicationContext`クラスのインスタンスが注入されます。（このクラスはSpring Bootプロジェクトにあります）

```java
@Autowired
private ResourceLoader resourceLoader;
// org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
```

**classpath**

`classpath:`から始める書き方をすると`ClassPathResource`クラスのインスタンスが注入されます。

```java
Resource resource = resourceLoader.getResource("classpath:config/app.properties");
// org.springframework.core.io.ClassPathResource
```

**url**

httpスキームで記述すると`UrlResource`クラスのインスタンスが注入されます。

```java
Resource resource = resourceLoader.getResource("http://localhost:8887/config/app.properties");
// org.springframework.core.io.UrlResource
```

**file**

[fileスキーム] (https://tools.ietf.org/html/rfc8089)で記述すると`FileUrlResource`クラスのインスタンスが注入されます。

```java
Resource resource = resourceLoader.getResource("file:///c://config/app.properties");
// org.springframework.core.io.FileUrlResource
```


## `@Value`アノテーションを使用する方法

Valueアノテーションの値により、Spring Frameworkが適切な実装クラスのインスタンスを注入してくれます。
アノテーションに記述する値のフォーマットはResourceLoaderと同じです。

**classpath**

```java
@Value("classpath:config/app.properties")
private Resource classpathResource;
// org.springframework.core.io.ClassPathResource
```

**url**

```java
@Value("http://localhost:8887/config/app.properties")
private Resource urlResource;
// org.springframework.core.io.UrlResource
```

**file**

```java
@Value("file:///c://config/app.properties")
private Resource fileUrlResource;
// org.springframework.core.io.FileUrlResource
```

## [ResourceUtils] (https://docs.spring.io/spring/docs/5.2.1.RELEASE/javadoc-api/org/springframework/util/ResourceUtils.html)を使用する方法

**Since 1.1.5**

> Utility methods for resolving resource locations to files in the file system. **Mainly for internal use within the framework.**

APIドキュメントに書かれている通り、主にSpring Framework内部で使用するためのユーティリティークラスです。アプリケーション側で使ってはいけないという記述はありませんが、あくまでもフレームワーク内での使用を目的としているので利用には留意が必要です。

**classpath**

```java
File file = ResourceUtils.getFile("classpath:config/app.properties");
```

**url**

```java
URL url = ResourceUtils.getURL("http://localhost:8887/config/app.properties");
```

**file**

```java
File file = ResourceUtils.getFile("file:///c://var//config/app.properties");
```

### [StreamUtils] (https://docs.spring.io/spring/docs/5.2.1.RELEASE/javadoc-api/org/springframework/util/StreamUtils.html)

**Since 3.2.2**

> Simple utility methods for dealing with streams. The copy methods of this class are similar to those defined in FileCopyUtils except that all affected streams are left open when done. All copy methods use a block size of 4096 bytes.
**Mainly for use within the framework, but also useful for application code.**

Spring Frameworkにはストリームを処理するユーティリティクラスもあります。このクラスのcopyToStringメソッドを使うと下記のようにコードを簡略化できます。

```java
String data = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
```