当前位置: 首页 > news >正文

spring-core 资源管理- Resource 接口讲解

        Spring Framework 是一个典型的分层架构,其源码由多个模块组成,每个模块都承担着清晰的职责。如果你要系统地学习和讲解 Spring Framework 的源码,最合理的切入顺序是按照依赖层次从“底层 → 上层”进行讲解和剖析。

今天我们来从最底层 spring-core 进行讲解和剖析。本次讲解的spring-framework组件版本 6.2.7。

一,Resource 资源接口讲解

Resource 是 spring 统一标准访问各种资源的核心接口,比如文件系统资源,类路径资源,URL资源等。它提供了一组统一的方法,比如getInputStream(),exists(),isOpen()等,让不同类型的资源可以以一致的方式被处理。详细的继承结构如下:

1.1 InputStreamSource 接口

在Resource 接口上面,还继承一个也是由Spring定义一个InputStream的输入流标准接口。代码如下:

package org.springframework.core.io;import java.io.IOException;
import java.io.InputStream;/*** @author Juergen Hoeller* @since 20.01.2004*/
@FunctionalInterface
public interface InputStreamSource {/*** 该接口定义了:返回底层资源内容的 InputStream。通常预期每个此类调用都会创建一个新流。* 也就是说,接口定义不允许返回 InputStream 为 null。原因为面对一些需要重复读取流,比如 JavaMail 该 API 需要能够多次读取流。* 对于此类用例,要求每个 getInputStream() 调用都返回一个新流。* @return 底层资源的输入流(不能为 null)* @throws java.io.FileNotFoundException 如果底层资源不存在* @throws IOException 如果无法打开内容流* @see Resource#isReadable()* @see Resource#isOpen()*/InputStream getInputStream() throws IOException;
}

Spring中的 InputStreamSource 定义其底层资源输入流(InputStream)获取标准,其中最重要的一条就是绝对不能返回为空,否则报异常:FileNotFoundException。这也是面对需要重复读取流数据的处理方式。直接返回一条新的流。

1.2 Resource 资源接口

代码如下:

package org.springframework.core.io;import org.springframework.lang.Nullable;
import org.springframework.util.FileCopyUtils;/*** @author Juergen Hoeller* @author Arjen Poutsma* @since 28.12.2003*/
public interface Resource extends InputStreamSource {/** 判断此资源是否在物理形式上实际存在 */boolean exists();/** 表示此资源的非空内容是否可以通过 {@link #getInputStream()} 读取。 */default boolean isReadable() {return exists();}/*** 表示此资源是否代表一个打开的流。* 如果为 {@code true},则 InputStream 不可以被多次读取,* 必须读取并关闭以避免资源泄漏。*/default boolean isOpen() {return false;}/*** 判断此资源是否表示文件系统中的一个文件。* @since 5.0*/default boolean isFile() {return false;}/*** 返回此资源的 URL* @throws IOException 如果资源不能解析为 URL,即资源不可用作为描述符*/URL getURL() throws IOException;/*** 返回此资源的 URI * @throws IOException 如果资源不能解析为 URI,即资源不可用作为描述符*/URI getURI() throws IOException;/*** 返回此资源的 File * @throws java.io.FileNotFoundException 如果资源不能解析为绝对路径文件,* 即资源在文件系统中不可用* @throws IOException 在解析/读取失败的情况下*/File getFile() throws IOException;/*** 返回一个 {@link ReadableByteChannel}。* <p>期望每次调用都创建一个新的通道。* <p>默认实现返回的是通过 {@link Channels#newChannel(InputStream)} * 使用 {@link #getInputStream()} 的结果创建的通道。* @return 底层资源的字节通道(必须不是 {@code null})* @throws java.io.FileNotFoundException 如果底层资源不存在* @throws IOException 如果内容通道无法打开* @since 5.0*/default ReadableByteChannel readableChannel() throws IOException {return Channels.newChannel(getInputStream());}/*** 将此资源的内容作为字节数组返回* @return 此资源的内容作为字节数组* @throws java.io.FileNotFoundException 如果资源不能解析为绝对路径文件,* 即资源在文件系统中不可用* @throws IOException 在解析/读取失败的情况下* @since 6.0.5*/default byte[] getContentAsByteArray() throws IOException {return FileCopyUtils.copyToByteArray(getInputStream());}/*** 使用指定的字符集将此资源的内容作为字符串返回。* @param charset 要用于解码的字符集* @return 此资源的内容作为字符串* @throws java.io.FileNotFoundException 如果资源不能解析为绝对路径文件,* 即资源在文件系统中不可用* @throws IOException 在解析/读取失败的情况下* @since 6.0.5*/default String getContentAsString(Charset charset) throws IOException {return FileCopyUtils.copyToString(new InputStreamReader(getInputStream(), charset));}/*** 确定此资源的内容长度。* @throws IOException 如果资源无法被解析(如不在文件系统或未知物理资源类型)*/long contentLength() throws IOException;/*** 确定此资源的最后修改时间戳。* @throws IOException 如果资源无法被解析(如不在文件系统或未知物理资源类型)*/long lastModified() throws IOException;/*** 创建相对于此资源的新资源。* @param relativePath 相对路径(相对于当前资源)* @return 相对资源的资源句柄* @throws IOException 如果相对资源无法被确定*/Resource createRelative(String relativePath) throws IOException;/*** 获取此资源的文件名 —— 通常是路径的最后一部分。例如,{@code "myfile.txt"}。* 如果此类资源没有文件名,则返回 {@code null}。* 建议实现类返回未编码的文件名。*/@NullableString getFilename();/*** 返回此资源的描述信息, 在处理资源时用于错误输出。* 建议实现类也在其 {@code toString} 方法中返回此值。*/String getDescription();}

Resource 接口通过统一抽象,简化了资源访问逻辑,是 Spring 中资源管理的核心。理解其方法含义和实现类特性,有助于高效处理文件、网络、类路径等资源。实际开发中,优先使用 ResourceLoader(如 ApplicationContext)获取资源,而非手动创建具体实现类。

1.3 UrlResource URL获取资源案例

案例准备:小编在spring-framework 源码里面单独建立一个spring-learn 模块,专门用于学习

UrlResource的作用 :表示通过 URL 访问的资源,当 URL 协议为 file:// 时支持写入。


/*** @author liuwq* @time 2025/6/12* @remark*/
public class UrlResourceExample {public static void main(String[] args) throws Exception {// 指定远程 URL 资源,获取 B站 首页资源Resource resource = new UrlResource("https://www.bilibili.com/");if (resource.exists()) {System.out.println("资源描述: " + resource.getDescription());try (InputStream is = resource.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {String line;while ((line = reader.readLine()) != null) {System.out.println(line);}}} else {System.out.println("资源无法访问或不存在!");}}
}

输出结果:B 站首页返回肯定的HTML内容

> Task :spring-learn:run
资源描述: URL [https://www.bilibili.com/]
<!DOCTYPE html><html><head>
<!-- Dejavu Release Version 64940-->
<script>
window._BiliGreyResult = {method: "direct",versionId: "64940",
}
</script><meta charset="UTF-8"><title>验证码_哔哩哔哩</title><meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minim
um-scale=1,viewport-fit=cover"><meta name="spm_prefix" content="333.1291"><script type="text/javascript" src="//www.bilibili.com/gentleman/polyfill.js?features=Promi
se%2CObject.assign%2CString.prototype.includes%2CNumber.isNaN"></script><script>window._riskdata_ = { 'v_voucher': 'voucher_9864655f-74b6-4614-baf4-e655eddbe721' }</
script><script type="text/javascript" src="//s1.hdslb.com/bfs/seed/log/report/log-reporter.js"></script><link href="//s1.hdslb.com/bfs/static/jinkela/risk-captcha/cs
s/risk-captcha.0.3bd977140b994afccbcc4d0102d33b9577e89cc0.css" rel="stylesheet"></head><body><div id="biliMainHeader"></div><div id="risk-captcha-app"></div><script 
src="//s1.hdslb.com/bfs/seed/jinkela/risk-captcha-sdk/CaptchaLoader.js"></script><script type="text/javascript" src="//s1.hdslb.com/bfs/static/jinkela/risk-captcha/1
.risk-captcha.3bd977140b994afccbcc4d0102d33b9577e89cc0.js"></script><script type="text/javascript" src="//s1.hdslb.com/bfs/static/jinkela/risk-captcha/risk-captcha.3bd977140b994afccbcc4d0102d33b9577e89cc0.js"></script></body></html>

1.4 FileSystemResource 文件系统资获取资源案例

作用:从文件系统读取资源

package com.toast.learn.spring.core.resource;import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;/*** @author liuwq* @time 2025/6/12* @remark*/
public class FileSystemResourceExample {public static void main(String[] args) throws Exception {// 指定文件系统的资源路径Resource resource = new FileSystemResource("D:\\work-space\\spring_GROUP\\spring-framework-6.2.7\\spring-learn\\doc\\与时俱进.txt");if (resource.exists()) {System.out.println("资源描述: " + resource.getDescription());try (InputStream is = resource.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(is));) {String line;while ((line = reader.readLine()) != null) {System.out.println(line);}}} else {System.out.println("资源不存在!");}}
}

与时俱进.txt 内容

输出结果:

> Task :spring-learn:run
资源描述: file [D:\work-space\spring_GROUP\spring-framework-6.2.7\spring-learn\doc\与时俱进.txt]
【当代话梗】芜湖~ 起飞咯
【当代话梗】有的兄弟有的
【当代话梗】包的包的
【当代话梗】那咋了
【21世纪名场面】王法官:人不是你撞的,你为什么要扶!

1.5 ClassPathResourceExample classpath获取资源案例

作用:读取 src/main/resources 目录下的配置文件(如 application.properties

package com.toast.learn.spring.core.resource;import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;/*** @author liuwq* @time 2025/6/12* @remark*/
public class ClassPathResourceExample {public static void main(String[] args) throws Exception {// 指定类路径下的资源Resource resource = new ClassPathResource("application.properties");if (resource.exists()) {System.out.println("资源描述: " + resource.getDescription());try (InputStream is = resource.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {String line;while ((line = reader.readLine()) != null) {System.out.println(line);}}} else {System.out.println("资源不存在!");}}
}

application.properties 的内容如下:

输出结果

> Task :spring-learn:run
资源描述: class path resource [application.properties]
toast.learn.spring.resource.classpath="Toast Learn Spring-framework"

1.6 ByteArrayResource 内存获取资源

基于内存的字节数组资源,虽然是只读资源,但可以通过继承或包装实现写入逻辑。

package com.toast.learn.spring.core.resource;import org.springframework.core.io.ByteArrayResource;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;/*** @author liuwq* @time 2025/6/12* @remark*/
public class ByteArrayResourceExample {public static void main(String[] args) {// 假设这是从某个地方获取的原始字节数据(比如网络、数据库)String data = "准备好学习 spring 了吗。\nHello, ByteArrayResource!";byte[] bytes = data.getBytes();// 使用 ByteArrayResource 包装字节数组ByteArrayResource resource = new ByteArrayResource(bytes);// 读取资源内容try (InputStream inputStream = resource.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {String line;while ((line = reader.readLine()) != null) {System.out.println(line); // 打印每一行}} catch (IOException e) {e.printStackTrace();}}
}

输出结果:

> Task :spring-learn:run
准备好学习 spring 了吗。
Hello, ByteArrayResource!

二,WritableResource 接口

WritableResource 接口,该接口是为了扩展 Resource 接口的写能力而提供的。

public interface WritableResource extends Resource {}

可以发现其有三个接口都做了写的扩展能力。使用PathResource做一次写案例

2.1 PathResource 写处理

该类通过 java.nio.file.Path 路径来获取文件位置,从而获取资源。以下案例是将写入一些指定数据到指定文件中:与时俱进-writer.txt

package com.toast.learn.spring.core.resource;import org.springframework.core.io.PathResource;import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;/*** @author liuwq* @time 2025/6/12* @remark*/
public class PathWritableResourceExample {public static void main(String[] args) {Path path = Paths.get("D:\\work-space\\spring_GROUP\\spring-framework-6.2.7\\spring-learn\\doc\\与时俱进-writer.txt");PathResource resource = new PathResource(path);if (resource.exists()) {System.out.println("资源描述: " + resource.getDescription());try (OutputStream os = resource.getOutputStream(); // 默认是覆盖内容PrintWriter writer = new PrintWriter(os)) {writer.println("【当代话梗】芜湖~ 起飞咯");writer.println("【当代话梗】有的兄弟有的");writer.println("【当代话梗】包的包的");writer.println("【当代话梗】那咋了");writer.println("【21世纪名场面】王法官:人不是你撞的,你为什么要扶!");} catch (Exception e) {e.printStackTrace();}} else {System.out.println("资源不存在!");}}
}

输出结果:

> Task :spring-learn:run
资源描述: path [D:\work-space\spring_GROUP\spring-framework-6.2.7\spring-learn\doc\与时俱进-writer.txt]

同样的道理,其中 FileSystemResource 子类也是通过文件系统来获取文件资源,也可以进行写入数据。而通过URL 的方式获取资源,则不可以进行写入,因为URL获取到的资源并非本地文件资源。要是真的支持写入,那不是成攻击别人的服务器的吗。案例如下:

2.2 FileUrlResource 写处理

package com.toast.learn.spring.core.resource;import org.springframework.core.io.FileUrlResource;
import org.springframework.core.io.PathResource;import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;/*** @author liuwq* @time 2025/6/12* @remark*/
public class FileUrlWritableResourceExample {public static void main(String[] args) {try {// 获取B 站资源URL url = new URL("https://www.bilibili.com/");FileUrlResource resource = new FileUrlResource(url);if (resource.exists()) {System.out.println("资源描述: " + resource.getDescription());try (OutputStream os = resource.getOutputStream(); // 往里面写数据PrintWriter writer = new PrintWriter(os)) {writer.println("【当代话梗】芜湖~ 起飞咯");writer.println("【当代话梗】有的兄弟有的");writer.println("【当代话梗】包的包的");writer.println("【当代话梗】那咋了");writer.println("【21世纪名场面】王法官:人不是你撞的,你为什么要扶!");} catch (Exception e) {e.printStackTrace();}} else {System.out.println("资源不存在!");}} catch (MalformedURLException e) {throw new RuntimeException(e);}}
}

输出结果:

> Task :spring-learn:run
资源描述: URL [https://www.bilibili.com/]
java.io.FileNotFoundException: URL [https://www.bilibili.com/] cannot be resolved to absolute file path because it does not reside in the file system: https://www.bilibili.com/at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:223)at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:210)at org.springframework.core.io.UrlResource.getFile(UrlResource.java:297)at org.springframework.core.io.FileUrlResource.getFile(FileUrlResource.java:84)at org.springframework.core.io.FileUrlResource.getOutputStream(FileUrlResource.java:102)at com.toast.learn.spring.core.resource.FileUrlWritableResourceExample.main(FileUrlWritableResourceExample.java:28)[Incubating] Problems report is available at: file:///D:/work-space/spring_GROUP/spring-framework-6.2.7/build/reports/problems/problems-report.html

你试图将一个 URL 资源(网络地址)当作本地文件路径来处理 ,但这个 URL 并不是本地文件系统中的资源。这些操作都试图把一个 网络资源 当作本地文件来处理,这是不允许的!

  • 不能处理网络地址(http:// 或 https://)
  • 它只能处理本地文件路径(如 file:/xxx/xxx
package com.toast.learn.spring.core.resource;import org.springframework.core.io.FileUrlResource;
import org.springframework.core.io.PathResource;import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;/*** @author liuwq* @time 2025/6/12* @remark*/
public class FileUrlWritableResourceExample {public static void main(String[] args) {try {URL url = new URL("file:/D:/work-space/spring_GROUP/spring-framework-6.2.7/spring-learn/doc/demo.txt");FileUrlResource resource = new FileUrlResource(url);if (resource.exists()) {System.out.println("资源描述: " + resource.getDescription());try (OutputStream os = resource.getOutputStream(); // 默认是覆盖内容PrintWriter writer = new PrintWriter(os)) {writer.println("【当代话梗】芜湖~ 起飞咯");writer.println("【当代话梗】有的兄弟有的");writer.println("【当代话梗】包的包的");writer.println("【当代话梗】那咋了");writer.println("【21世纪名场面】王法官:人不是你撞的,你为什么要扶!");} catch (Exception e) {e.printStackTrace();}} else {System.out.println("资源不存在!");}} catch (MalformedURLException e) {throw new RuntimeException(e);}}
}

输出结果:

三,ContextResource 接口

ContextResource 是 Spring 框架中的一个扩展接口 ,它继承自 Resource 接口,定义在 spring-core 模块中。

它的作用是:为资源提供相对于某个上下文路径(context-relative)的定位能力。

通俗点说,就是这个资源知道它是在哪个“上下文”中加载的,比如它是从类路径下加载的、还是从 Web 应用上下文中加载的等。

在 Resource 接口已经定义读取资源相关信息的标准接口,比如获取文件的数据流,文件是否存在,是否被已被流打开进行读取。

public interface ContextResource extends Resource {/** 获取该资源在上下文路径内的相对路径 */String getPathWithinContext();
}

资源位置

上下文路径

getPathWithinContext()返回值

classpath:config/app.properties

classpath:

config/app.properties

file:/data/config/db.properties

file:/data/

config/db.properties

webapp/WEB-INF/web.xml

webapp/WEB-INF/

web.xml

🧠 3.1、什么是“上下文”?

“上下文”本质上是指:程序运行时所处的环境或状态信息。

它包含了当前操作所需要的所有相关信息 ,比如:

  • 配置信息
  • 资源路径
  • 当前用户身份
  • 应用的状态
  • Bean 的定义和实例
  • 环境变量

你可以把它想象成一个人说话的“背景环境”,你只有了解了这个背景,才能正确理解他说的话。

📌 3.2、不同场景下的“上下文”

3.2.1 Spring 中的 ApplicationContext

这是最常提到的“上下文”。

ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
  • 它是 Spring 容器的核心接口。
  • 包含了所有 Bean 的定义、配置、环境信息等。
  • 提供了获取 Bean、发布事件、加载资源等功能。

📌 所以,当你看到 ContextResource 接口中的方法 getPathWithinContext(),它的意思是:

这个资源在这个 Spring 容器(ApplicationContext)中的相对路径。

例如:

new ClassPathResource("config/app.properties");

它的 getPathWithinContext() 就是 "config/app.properties",表示在类路径上下文中的位置

3.2.2 Web 应用中的上下文(ServletContext)

在 Java Web 开发中(如 Servlet 或 Spring MVC),也有一个叫 ServletContext 的接口。

    • 它代表整个 Web 应用的上下文。
    • 可以用来获取 Web 应用的路径、初始化参数、全局属性等。

举个例子:

ServletContext context = request.getServletContext();
String realPath = context.getRealPath("/WEB-INF/web.xml");

这里的“上下文”就是指整个 Web 应用运行的环境。

3.2.3 线程上下文(ThreadLocal)

有时候我们会说“线程上下文”,指的是每个线程私有的数据存储空间。

ThreadLocal<User> currentUser = new ThreadLocal<>();
currentUser.set(new User("Toast"));

这里的 ThreadLocal 就是一种线程级别的“上下文”,用于保存线程独有的状态信息。


3.2.4 HTTP 请求上下文(Request Context)

在处理 HTTP 请求时,我们也会提到“请求上下文”,它通常包括:

  • 请求头(Headers)
  • 请求参数(Params)
  • 用户会话(Session)
  • 当前登录用户(Principal)

这些信息构成了一个完整的请求“上下文”。


🧩 3.3、通俗解释一下“上下文”

你可以把“上下文”理解为:

“你现在在哪里?你是谁,你能访问什么?你知道哪些信息”

就像你在公司上班时,你的工作环境(电脑、网络、权限、同事)就构成了你的“办公上下文”。如果你换到了家里,你的“家庭上下文”就不同了。


✅ 3.4、总结:“上下文”的核心思想

特性

说明

含义

表示程序运行时所依赖的环境信息

作用

提供运行时所需的数据、配置、状态等

示例

ApplicationContext,ServletContext,ThreadLocal,RequestContext

在 Spring 中

是容器的核心,负责管理 Bean 和资源

四,Resource 接口继承结构分析

4.1 AbstractResource 抽象类

Resource 接口有一个公共抽象类 AbstractResource 承担着模板方法的作用,也就是说该公共抽象类定义一套公共的业务流程默认执行标准。比如:判断文件是否可读默认就是判断文件是否存在:

package org.springframework.core.io;import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.function.Supplier;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;import org.springframework.lang.Nullable;
import org.springframework.util.ResourceUtils;/*** @author Juergen Hoeller* @author Sam Brannen* @since 28.12.2003*/
public abstract class AbstractResource implements Resource {@Overridepublic boolean exists() {// Try file existence: can we find the file in the file system?if (isFile()) {try {return getFile().exists();}catch (IOException ex) {debug(() -> "Could not retrieve File for existence check of " + getDescription(), ex);}}// Fall back to stream existence: can we open the stream?try {getInputStream().close();return true;}catch (Throwable ex) {debug(() -> "Could not retrieve InputStream for existence check of " + getDescription(), ex);return false;}}@Overridepublic boolean isReadable() { return exists(); }@Overridepublic boolean isOpen() { return false; }@Overridepublic boolean isFile() { return false; }@Overridepublic URL getURL() throws IOException {throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");}@Overridepublic URI getURI() throws IOException {URL url = getURL();try {return ResourceUtils.toURI(url);}catch (URISyntaxException ex) {throw new IOException("Invalid URI [" + url + "]", ex);}}@Overridepublic File getFile() throws IOException {throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");}@Overridepublic ReadableByteChannel readableChannel() throws IOException {return Channels.newChannel(getInputStream());}// 忽略其他业务方法
}

4.2 AbstractFileResolvingResource 抽象类

org.springframework.core.io.AbstractFileResolvingResource 是 Spring 框架中用于抽象资源处理 的一个核心类,它位于 spring-core 模块中。

AbstractFileResolvingResource 是一个抽象类,提供了将资源解析为 java.io.File 对象的能力。

🔍 主要作用

✅ 1. 提供统一的 File 解析方法

✅ 2. 判断是否是文件系统资源

✅ 3. 支持文件路径转换

package org.springframework.core.io;import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.NoSuchFileException;
import java.nio.file.StandardOpenOption;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;import org.springframework.util.ResourceUtils;/*** @author Juergen Hoeller* @since 3.0*/
public abstract class AbstractFileResolvingResource extends AbstractResource {/** 忽略部分方法 */@Overridepublic boolean isFile() {try {URL url = getURL();if (url.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {return VfsResourceDelegate.getResource(url).isFile();}return ResourceUtils.URL_PROTOCOL_FILE.equals(url.getProtocol());}catch (IOException ex) {return false;}}@Overridepublic File getFile() throws IOException {URL url = getURL();if (url.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {return VfsResourceDelegate.getResource(url).getFile();}return ResourceUtils.getFile(url, getDescription());}@Overrideprotected File getFileForLastModifiedCheck() throws IOException {URL url = getURL();if (ResourceUtils.isJarURL(url)) {URL actualUrl = ResourceUtils.extractArchiveURL(url);if (actualUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {return VfsResourceDelegate.getResource(actualUrl).getFile();}return ResourceUtils.getFile(actualUrl, "Jar URL");}else {return getFile();}}protected boolean isFile(URI uri) {try {if (uri.getScheme().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {return VfsResourceDelegate.getResource(uri).isFile();}return ResourceUtils.URL_PROTOCOL_FILE.equals(uri.getScheme());}catch (IOException ex) {return false;}}protected File getFile(URI uri) throws IOException {if (uri.getScheme().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {return VfsResourceDelegate.getResource(uri).getFile();}return ResourceUtils.getFile(uri, getDescription());}@Overridepublic ReadableByteChannel readableChannel() throws IOException {try {// Try file system channelreturn FileChannel.open(getFile().toPath(), StandardOpenOption.READ);}catch (FileNotFoundException | NoSuchFileException ex) {// Fall back to InputStream adaptation in superclassreturn super.readableChannel();}}@Overridepublic long contentLength() throws IOException {URL url = getURL();if (ResourceUtils.isFileURL(url)) {// Proceed with file system resolutionFile file = getFile();long length = file.length();if (length == 0L && !file.exists()) {throw new FileNotFoundException(getDescription() +" cannot be resolved in the file system for checking its content length");}return length;}else {// Try a URL connection content-length headerURLConnection con = url.openConnection();customizeConnection(con);if (con instanceof HttpURLConnection httpCon) {httpCon.setRequestMethod("HEAD");}long length = con.getContentLengthLong();if (length <= 0 && con instanceof HttpURLConnection httpCon &&httpCon.getResponseCode() == HttpURLConnection.HTTP_BAD_METHOD) {con = url.openConnection();customizeConnection(con);length = con.getContentLengthLong();}return length;}}@Overridepublic long lastModified() throws IOException {URL url = getURL();boolean fileCheck = false;if (ResourceUtils.isFileURL(url) || ResourceUtils.isJarURL(url)) {// Proceed with file system resolutionfileCheck = true;try {File fileToCheck = getFileForLastModifiedCheck();long lastModified = fileToCheck.lastModified();if (lastModified > 0L || fileToCheck.exists()) {return lastModified;}}catch (FileNotFoundException ex) {// Defensively fall back to URL connection check instead}}// Try a URL connection last-modified headerURLConnection con = url.openConnection();customizeConnection(con);if (con instanceof HttpURLConnection httpCon) {httpCon.setRequestMethod("HEAD");}long lastModified = con.getLastModified();if (lastModified == 0) {if (con instanceof HttpURLConnection httpCon &&httpCon.getResponseCode() == HttpURLConnection.HTTP_BAD_METHOD) {con = url.openConnection();customizeConnection(con);lastModified = con.getLastModified();}if (fileCheck && con.getContentLengthLong() <= 0) {throw new FileNotFoundException(getDescription() +" cannot be resolved in the file system for checking its last-modified timestamp");}}return lastModified;}/** 忽略部分方法 */
}

AbstractResource 和 AbstractFileResolvingResource 都是抽象类,其作用都是承担公共业务流程的实现。AbstractResource 是所有Resource 资源可用的通用抽象类。而 AbstractFileResolvingResource 抽象类是承担着资源转File 统一解析业务流程。其业务颗粒度更细,是为统一解析File 而提供的标准接口。

AbstractFileResolvingResource

子类

是否继承AbstractFileResolvingResource

特点

FileSystemResource

表示文件系统中的文件,可完全解析为File

PathResource

使用 Java NIO 的Path,更现代的方式

UrlResource

如果 URL 是file:开头,可解析为File

ClassPathResource

如果资源在文件系统中,也可解析为File

ByteArrayResource

内存资源,不能转成File

五,其他的Resource 资源

提问,资源除了常见文件,网络,以及项目中的resource静态资源基本上都可以转成File 。还有什么类型的资源不是文件的?

当然是有的了,比如业务资源。就spring 框架内部也有许多业务需要为其管理资源,比如说Bean 对象的资源,比如模块资源管理。这些非可转File 资源的业务。其实就用不到 AbstractFileResolvingResource 这个抽象类。而是使用 AbstractResource。

假设说Bean资源来源很多,需要做一个Bean 资源监控业务类。也可以为其业务追加一个公共的抽象类,比如类名:AbstractBeanResourceMonitor

那么想实现Bean资源监控都可以通过这个类进行实现与进一步的处理。当然这是只是假设。Spring里面并没有提供这样的类,AbstractBeanResourceMonitor。

相关文章:

  • 历史项目依赖库Bugfix技巧-类覆盖
  • 正则表达式详解:从基础到高级应用的全面指南
  • 【文件】Linux 内核优化实战 - fs.inotify.max_user_watches
  • 基本进程调度算法
  • 马斯克YC技术核弹全拆解:Neuralink信号编译器架构·星舰着陆AI代码·AGI防御协议(附可复现算法核心/开源替代方案/中国技术对标路径)
  • anchor 智能合约 IDL 调用
  • 【信创-k8s】银河麒麟V10国防版+鲲鹏/飞腾(arm64架构)在线/离线部署k8s1.30+kubesphere
  • 《汇编语言:基于X86处理器》第4章 数据传送、寻址和算术运算(1)
  • Python----OpenCV(图像増强——图像平滑、均值滤波、高斯滤波、中值滤波、双边滤波)
  • RealSense 相机 | 读取IMU | 解决权限问题 | 提供示例程序
  • 阿里云无影:开启云端办公娱乐新时代
  • Re:从零开始的地址映射基本分页存储管理方式(考研向)
  • 设计模式 - 抽象工厂
  • 域名解析(DNS)相关
  • Typora - Typora 表格删除列与行
  • 光伏电站 “智慧大脑”:安科瑞 Acrel-1000DP 分布式监控系统技术解析
  • react gsap动画库使用详解之text文本动画
  • zlibrary镜像网站,zlibrary中文入口(持续更新)
  • 【Java开发日记】我们详细讲解一下 Java 中 new 一个对象的流程
  • JavaWeb学习——day8(MVC模式与session、cookie)
  • 做奶茶的网站怎么来做优化/百度应用商店app下载
  • dedecms 网站地图xml/企业网站推广的方法有哪些
  • 购车网站设计/百度流量统计
  • 网站浮动qq客服代码/百度app最新版本
  • python人狗大csdn/网站自然排名怎么优化
  • 营销网站报备/郑州网站关键词排名