Resource源码分析

master
xuchengsheng 2023-10-30 15:48:47 +08:00
parent 346cdb74ec
commit 6752216754
1 changed files with 80 additions and 31 deletions

View File

@ -15,35 +15,39 @@
- [七、常见问题和解决方法](#七常见问题和解决方法) - [七、常见问题和解决方法](#七常见问题和解决方法)
- [八、参考资料](#八参考资料) - [八、参考资料](#八参考资料)
### 一、知识储备
### 一、简介 1. **I/O知识**
+ 了解文件、路径、输入/输出流等基础概念。
2. **类路径Classpath**
+ 了解什么是类路径,以及如何从类路径中加载资源。
3. **URL和URI概念**
+ 这对于理解如何从网络或其他协议中加载资源是必要的。
- `Resource` 是 Spring 框架中用于简化和统一对底层资源如文件、classpath 资源、URL 等)的访问的一个核心接口。它为不同来源的资源提供了一个共同的抽象,并隐藏了具体资源访问的细节。 ### 二、简介
- 在 Java 开发中,资源的访问是常见的需求,如读取配置文件、图片、音频等。但 Java 的标准库为不同类型的资源提供了不同的访问机制:例如,对于文件系统中的资源,我们可能使用 `java.io.File`;对于 classpath 中的资源,我们可能使用 `ClassLoader``getResource``getResourceAsStream` 方法;对于网络资源,我们可能使用 `java.net.URL` `Resource` 是 Spring 框架中用于简化和统一对底层资源如文件、classpath 资源、URL 等)的访问的一个核心接口。它为不同来源的资源提供了一个共同的抽象,并隐藏了具体资源访问的细节。在 Java 开发中,资源的访问是常见的需求,如读取配置文件、图片、音频等。但 Java 的标准库为不同类型的资源提供了不同的访问机制:例如,对于文件系统中的资源,我们可能使用 `java.io.File`;对于 classpath 中的资源,我们可能使用 `ClassLoader``getResource``getResourceAsStream` 方法;对于网络资源,我们可能使用 `java.net.URL`这些不同的机制意味着我们需要了解和使用多种方式来访问资源这导致的问题是代码复杂性增加、重复代码以及可能的错误。为了提供一个统一、简化和更高级的资源访问机制Spring 框架引入了 `Resource` 接口,这个接口为所有的资源提供了一个统一的抽象。
这些不同的机制意味着开发者需要了解和使用多种方式来访问资源这导致的问题是代码复杂性增加、重复代码以及可能的错误。为了提供一个统一、简化和更高级的资源访问机制Spring 框架引入了 `Resource` 接口,这个接口为所有的资源提供了一个统一的抽象。 ### 三、主要功能
### 二、主要功能 1. **统一的资源抽象**
- **统一的资源抽象**
- 无论资源来自于文件系统、classpath、URL 还是其他来源,`Resource` 接口都为其提供了一个统一的抽象。 - 无论资源来自于文件系统、classpath、URL 还是其他来源,`Resource` 接口都为其提供了一个统一的抽象。
- **资源描述** 2. **资源描述**
- 通过 `getDescription()` 方法,每个 `Resource` 实现都可以为其所代表的底层资源提供描述性信息,这对于错误处理和日志记录特别有用。 - 通过 `getDescription()` 方法,每个 `Resource` 实现都可以为其所代表的底层资源提供描述性信息,这对于错误处理和日志记录特别有用。
- **读取能力** 3. **读取能力**
- `Resource` 提供了 `getInputStream()` 方法,允许直接读取资源内容,而无需关心资源的实际来源。 - `Resource` 提供了 `getInputStream()` 方法,允许直接读取资源内容,而无需关心资源的实际来源。
- **存在性与可读性** 4. **存在性与可读性**
- `Resource` 提供了 `exists()``isReadable()` 方法来确定资源是否存在及其是否可读。 - `Resource` 提供了 `exists()``isReadable()` 方法来确定资源是否存在及其是否可读。
- **开放性检查** 5. **开放性检查**
- `isOpen()` 方法用于检查资源是否表示一个已经打开的流,这有助于避免重复读取流资源。 - `isOpen()` 方法用于检查资源是否表示一个已经打开的流,这有助于避免重复读取流资源。
- **URI 和 URL 访问** 6. **URI 和 URL 访问**
- `Resource` 允许通过 `getURI()``getURL()` 方法获取其底层资源的 URI 和 URL这为进一步的资源处理提供了可能。 - `Resource` 允许通过 `getURI()``getURL()` 方法获取其底层资源的 URI 和 URL这为进一步的资源处理提供了可能。
- **文件访问** 7. **文件访问**
- 当资源代表一个文件系统中的文件时,可以通过 `getFile()` 直接访问该文件。 - 当资源代表一个文件系统中的文件时,可以通过 `getFile()` 直接访问该文件。
- **多种实现** 8. **多种实现**
- Spring 提供了多种 `Resource` 的实现,以支持不同来源的资源,如 `ClassPathResource`、`FileSystemResource` 和 `UrlResource` 等。 - Spring 提供了多种 `Resource` 的实现,以支持不同来源的资源,如 `ClassPathResource`、`FileSystemResource` 和 `UrlResource` 等。
### 、接口源码 ### 、接口源码
`InputStreamSource` 是一个简单的接口用于提供一个输入流。它被设计为可以多次返回一个新的、未读取的输入流这对于那些需要多次读取输入流的API。 `InputStreamSource` 是一个简单的接口用于提供一个输入流。它被设计为可以多次返回一个新的、未读取的输入流这对于那些需要多次读取输入流的API。
@ -56,7 +60,7 @@ public interface InputStreamSource {
/** /**
* 返回基础资源内容的 InputStream。 * 返回基础资源内容的 InputStream。
* 期望每次调用都会创建一个新的流。 * 期望每次调用都会创建一个新的流。
* 当考虑到像 JavaMail 这样的API时这个要求尤为重要因为在创建邮件附件时JavaMail需要能够多次读取流。对于这样的用例要求每个 getInputStream() 调用都返回一个新的流。 * 当我们考虑到像 JavaMail 这样的API时这个要求尤为重要因为在创建邮件附件时JavaMail需要能够多次读取流。对于这样的用例要求每个 getInputStream() 调用都返回一个新的流。
* @return 基础资源的输入流(不能为 null * @return 基础资源的输入流(不能为 null
* @throws java.io.FileNotFoundException 如果基础资源不存在 * @throws java.io.FileNotFoundException 如果基础资源不存在
* @throws IOException 如果无法打开内容流 * @throws IOException 如果无法打开内容流
@ -154,13 +158,18 @@ public interface Resource extends InputStreamSource {
} }
``` ```
### 、主要实现 ### 、主要实现
- `ClassPathResource`: 用于加载 classpath 下的资源。 1. `ClassPathResource`
- `FileSystemResource`: 用于访问文件系统中的资源。 + 用于加载 classpath 下的资源。
- `UrlResource`: 用于基于 URL 的资源。 2. `FileSystemResource`
- `ServletContextResource`: 用于 Web 应用中的资源。 + 用于访问文件系统中的资源。
- `ByteArrayResource` & `InputStreamResource`: 基于内存和流的资源表示。 3. `UrlResource`
+ 用于基于 URL 的资源。
4. `ServletContextResource`
+ 用于 Web 应用中的资源。
5. `ByteArrayResource` & `InputStreamResource`
+ 基于内存和流的资源表示。
~~~mermaid ~~~mermaid
classDiagram classDiagram
@ -214,7 +223,7 @@ classDiagram
ServletContextResource ..|> AbstractFileResolvingResource ServletContextResource ..|> AbstractFileResolvingResource
~~~ ~~~
### 、最佳实践 ### 、最佳实践
#### `ClassPathResource` #### `ClassPathResource`
@ -235,12 +244,12 @@ public class ClassPathResourceDemo {
#### `FileSystemResource` #### `FileSystemResource`
使用 `FileSystemResource` 是 Spring 框架中的一个组件,用于访问文件系统上的文件。在此示例中,`path` 变量存储了文件的完整路径。这个路径需要被替换为自己的有效文件路径。 使用 `FileSystemResource` 是 Spring 框架中的一个组件,用于访问文件系统上的文件。在此示例中,`path` 变量存储了文件的完整路径。这个路径需要被替换为我们自己的有效文件路径。
```java ```java
public class FileSystemResourceDemo { public class FileSystemResourceDemo {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
// 请替换自己的目录 // 请替换我们自己的目录
String path = "D:\\idea-work-space-xcs\\spring-reading\\spring-resources\\spring-resource\\myfile.txt"; String path = "D:\\idea-work-space-xcs\\spring-reading\\spring-resources\\spring-resource\\myfile.txt";
Resource resource = new FileSystemResource(path); Resource resource = new FileSystemResource(path);
try (InputStream is = resource.getInputStream()) { try (InputStream is = resource.getInputStream()) {
@ -301,14 +310,54 @@ public class InputStreamResourceDemo {
} }
``` ```
### 、与其他组件的关系 ### 、与其他组件的关系
- 例如:`ResourceLoader` 和 `ResourcePatternResolver` #### BeanFactory&ApplicationContext
### 七、常见问题和解决方法 `BeanFactory`和应用上下文`ApplicationContext`在实例化和配置Bean时通常需要访问资源。`Resource` 接口提供了一种统一的方式来加载资源文件这对于配置和初始化Bean非常有用。Bean定义中可以包含资源引用使Bean能够使用这些资源。
- 列出与此接口相关的常见问题及其解决方法。 #### ResourceLoader
### 八、参考资料 Spring提供了`ResourceLoader`接口,该接口在应用上下文中广泛使用,以便加载资源。`ResourceLoader`的默认实现是`DefaultResourceLoader`,它基于`Resource`接口实现了资源加载功能。通过`ResourceLoader`应用可以轻松获取和管理资源无论资源是来自文件系统、类路径、URL还是其他来源。
- 提供进一步的阅读或学习资料。 #### PropertyPlaceholderConfigurer
`PropertyPlaceholderConfigurer`是Spring框架中用于替换属性占位符的类。它可以将属性值从资源文件中读取然后替换配置文件中的占位符。这是通过 `locations` 属性指定的资源文件实现的。
#### MVC框架
Spring的MVC框架如Spring MVC通常需要处理文件上传和静态资源。`Resource` 接口及其实现可以用于管理和提供这些资源。`Resource`接口与`ResourceLoader`一起被用于加载静态资源例如图像、样式表和JavaScript文件。
#### 自定义资源加载和处理
我们自己也可以使用 `Resource` 接口自定义资源加载和处理逻辑。例如,我们可以创建一个自定义的 `Resource` 实现,用于加载资源文件,执行特定的处理逻辑,然后将处理后的资源提供给应用程序。
### 八、常见问题
1. **如何选择合适的 `Resource` 实现?**
- `ClassPathResource`: 用于访问类路径下的资源。
- `FileSystemResource`: 用于访问文件系统中的资源。
- `UrlResource`: 用于基于URL的资源如HTTP或FTP资源。
- `ServletContextResource`: 专为 Web 应用程序设计,用于访问`ServletContext`中的资源。
2. **资源未找到**
- 如果尝试使用一个不存在的路径或URL创建资源可能会得到一个 `FileNotFoundException`
- 确保提供正确的路径,并检查资源是否真的存在。
3. **如何处理编码或字符集问题?**
- 当从资源中读取文本内容时,可能需要处理编码问题。
- 使用 `Reader` 和适当的字符集,或使用 Spring 的 `EncodedResource` 类。
4. **相对路径的使用**
- 当使用 `FileSystemResource` 时,相对路径可能会导致混淆。确保我们了解相对路径的基准。
5. **资源的实际URL或文件路径是什么**
- 虽然 `Resource` 接口为各种资源类型提供了一个统一的抽象,但有时可能需要知道资源的真实类型或位置。
- 使用 `resource.getURL()``resource.getFile()` 可以尝试获取资源的真实URL或文件。
6. **如何在非Web应用程序中使用 `ServletContextResource`**
- 这是不可能的,因为 `ServletContextResource` 是为Web应用程序设计的。如果尝试在非Web应用程序中使用它将会得到错误。
7. **如何从Jar文件或War文件中读取资源**
- 使用 `ClassPathResource``UrlResource` 可以轻松地从Jar或War文件中读取资源。
- 但是对于嵌套的Jar文件例如当使用Spring Boot可执行Jar时需要特殊的处理通常通过 `org.springframework.boot.loader.jar.JarFile` 类。
8. **如何刷新或重新加载已更改的资源?**
- 默认情况下,`Resource` 实例不提供刷新或重新加载机制。但对于某些资源类型,如 `UrlResource`,每次调用 `getInputStream()` 都会重新读取内容。
- 对于需要刷新的资源,考虑使用缓存机制或其他方法来处理。
9. **资源加载的性能问题**
- 大量频繁地加载资源可能会导致性能问题。
- 考虑缓存资源内容或使用更高效的资源加载策略。