Spring系列18:Resource接口及内置实现

Java 的标准 java.net.URL 类和各种 URL 前缀的标准处理程序不足以满足所有对低级资源的访问。 例如没有标准化的 URL 实现可用于访问需要从类路径或相对于 ServletContext 获取的资源。 虽然可以为专门的 URL 前缀注册新的处理程序(类似于现有的前缀处理程序如 http:),但这通常相当复杂,并且 URL 接口仍然缺乏一些理想的功能,例如检查是否存在的方法 指向的资源。

针对上述这种情况,Spring 提供了更强大的接口 Resource用于对低级资源的抽象访问,其定义和主要接口方法说明如下。

package org.springframework.core.io;

public interface Resource extends InputStreamSource {

    // 特定资源是否存在
    boolean exists();

    // 内容非空可通过#getInputStream()读取
    default boolean isReadable() {
        return exists();
    }

    // 资源对应的InputStream是否已经打开,不能多次读取,应读取后关闭避免资源泄漏
    default boolean isOpen() {
        return false;
    }

    // 是否是 File 类型,配合 #getFile()
    default boolean isFile() {
        return false;
    }

    // 获取 URL
    URL getURL() throws IOException;

    // 获取URI
    URI getURI() throws IOException;

    // 获取 File
    File getFile() throws IOException;

    // 资源内容长度
    long contentLength() throws IOException;

    // 上次修改的时间戳
    long lastModified() throws IOException;

    // 给定路径创建资源
    Resource createRelative(String relativePath) throws IOException;

    // 获取文件名非全路径
    @Nullable
    String getFilename();

    // 获取资源描述
    String getDescription();

}

Resource接口继承了 InputStreamSource,其定义如下。

package org.springframework.core.io;

public interface InputStreamSource {
    // 获取资源对应的 InputStream
    InputStream getInputStream() throws IOException;
}

Resource接口并不是用于完全取代 java.net.URL,而是尽可能地通过其实现类来包装 URL进行处理,如 UrlResource 包装一个 URL 并使用包装的 URL 来完成它的的功能。具体看下一节的内置实现。

UrlResource 包装了 java.net.URL,可用于访问通常可通过 URL 访问的任何对象,例如文件、HTTP 目标、FTP 目标等。这些资源通过标准前缀来区分, file:用于访问文件系统路径, http:用于通过 HTTP 协议访问资源, ftp:用于通过 FTP 访问资源等。

ClassPathResource表示应从类路径获取的资源。它使用线程上下文类加载器、给定的类加载器或给定的类来加载资源。可通过特定前缀 classpath:指定为该资源。

如果类路径资源驻留在文件系统中,则此 Resource 实现支持解析为 java.io.File,但不支持在 jar 包中且尚未解压的类路径资源。

这是 java.io.Filejava.nio.file.Path 句柄的资源实现。它支持作为 FileURL 的解析。

这是 ServletContext 资源的 Resource 实现,它解释相关 Web 应用程序根目录中的相对路径。

它始终支持流访问和 URL 访问,但仅在扩展 Web 应用程序存档并且资源物理位于文件系统上时才允许 java.io.File 访问。它是否被解压并在文件系统上或直接从 JAR 或其他地方(如数据库)访问实际上取决于 Servlet 容器。

给定字节数组的资源实现。它为给定的字节数组创建一个 ByteArrayInputStream。对于从任何给定的字节数组加载内容很有用,而不必求助于单一使用的 InputStreamResource

InputStreamResource 是给定 InputStream 的资源实现。首选 ByteArrayResource 或任何基于文件的 Resource 实现,仅当没有特定的 Resource 实现适用时才应使用它。

ResourceLoader 用于加载资源(例如类路径或文件系统资源)的策略接口。

package org.springframework.core.io;

public interface ResourceLoader {

    /** Pseudo URL prefix for loading from the class path: "classpath:". */
    String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;

    // 返回指定资源位置的资源句柄
    Resource getResource(String location);

    // 获取 ClassLoader
    // 需要直接访问 ClassLoader 的客户端可以通过 ResourceLoader 统一的方式进行访问,而不是依赖于线程上       下文 ClassLoader。
    @Nullable
    ClassLoader getClassLoader();

}

源码中对于的说明:

  • 句柄应始终是可重用的资源描述符,允许多个 Resource.getInputStream() 调用。
  • 必须支持完全限定的 URL,例如” file:C:/test.dat“。
  • 必须支持类路径伪 URL,例如” classpath:test.dat“。
  • 应该支持相对文件路径,例如” WEB-INF/test.dat“。 (这将是特定于实现的,通常由 ApplicationContext 实现提供。)
  • 资源句柄并不意味着现有资源;需要调用 Resource.exists() 来检查是否存在。

所有 org.springframework.context.ApplicationContext都必须实现该 ResourceLoader接口。调用 getResource()接口的返回值类型取决于当前context的类型,当前context会自动转换。如下案例。

Resource template = ctx.getResource("some/resource/path/myTemplate.txt");

ctxClassPathXmlApplicationContext返回 ClassPathResource

ctxFileSystemXmlApplicationContext返回 FileSystemResource

如ctx 是WebApplicationContext返回 ServletContextResource

如果不想依赖 context类型决定返回资源的类型,可以指定前缀的方式,强制返回特定类型的资源。如下案例。

Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
// 返回 ClassPathResource
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
// 返回 UrlResource

指定前缀对于资源加载方式和返回的影响对应关系如下表。

前缀 例子 如何解析 classpath:

从类路径加载 file: file:///data/config.xml 从文件系统作为</p> <p>加载 http:</p> <p>从文件系统作为</p> <p>加载 (none)</p> <p>取决于底层的</p> <p><code>ResourceLoaderAware</code> 接口是一个特殊的回调接口,它标识期望提供 <code>ResourceLoader</code> 引用的组件。其定义如下。</p> <pre><code class="language-java">package org.springframework.context; public interface ResourceLoaderAware extends Aware { // 可自定义保存一个ResourceLoader的引用来进行资源加载 void setResourceLoader(ResourceLoader resourceLoader); } </code></pre> <p>由于 <code>org.springframework.context.ApplicationContext</code>实现了 <code>ResourceLoader</code>,因此 bean 还可以实现 <code>ApplicationContextAware</code> 接口并直接使用提供的应用程序上下文来加载资源。从面向接口编程和解耦的角度来说,需要 <code>ResourceLoader</code>的来进行资源加载,更推荐实现ResourceLoaderAware 接口。</p> <p>深一层思考,如果容器中的一个bean需要一个 <code>ResourceLoader</code>依赖用于加载资源,除了实现 <code>ResourceLoaderAware</code>,是否还有其它方式呢?</p> <p><code>ResourceLoader</code>实例会自动注入到IoC容器,我们可通过构造函数、setter方法、@Autowire注解等方式,直接注入该依赖,具体看一看之前Ioc相关的文章。</p> <p>如果某个bean依赖于特定的 <code>Resource</code>,那么我们如何快速优雅地注入该依赖呢?直接上代码。</p> <pre><code class="language-java">public class MyConfig { /** * 通过配置文件注入的资源 */ private Resource template; public Resource getTemplate() { return template; } public void setTemplate(Resource template) { this.template = templa } } </code></pre> <p><code>MyConfig</code>依赖一个类文件路径下的一个配置文件。</p> <p>对应的xml文件 <code>spring.xml</code>和配置文 <code>db.properties</code>件如下:</p> <pre><code class="language-xml"> </code></pre> <pre><code class="language-properties">url=jdbc:mysql://localhost/test username=root password=456 max=1024 </code></pre> <p>运行主程序如下:</p> <pre><code class="language-java">package com.crab.spring.ioc.demo02; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class ResourceTest { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring" + ".xml"); MyConfig myConfig = context.getBean(MyConfig.class); Resource template = myConfig.getTemplate(); System.out.println(template); System.out.println(template instanceof ClassPathResource); } } </code></pre> <p>运行结果:</p> <pre><code class="language-java">class path resource [db.properties] true class path resource [db.properties] </code></pre> <ul> <li>成功注入了 <code>Resourece</code>资源到 <code>MyConfig</code>的bean中</li> <li>注入 <code>Resource</code>实际是我们通过 <code>ClassPathXmlApplicationContext</code>加载的 <code>ClassPathResource</code></li> </ul> <p>扩展:结合上一篇的资源特定前缀和 <code>ApplicationContext</code>的关系,忘了请翻看下,我们也可以指定前缀来加载特定的资源。较为简单就不演示了。</p> <pre><code class="language-xml"> file:///some/resource/path </code></pre> <pre><code class="language-xml"> // 注入的是FileSystemResource </code></pre> <p>回顾一下, <code>ResourceLoader</code>章节,我们分析了指定资源路径前缀对于资源加载方式的影响,对应关系如下表。下面将分带前缀和不带前缀的方式来配置应用上下文。</p> <p>前缀 例子 如何解析 classpath:</p> <p>从类路径加载 file: :///data/config.xml 从文件系统作为

加载 http:

从文件系统作为

加载 (none)

取决于底层的

ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");

上面这个案例资源路径不带前缀, ClassPathXmlApplicationContext将使用 ClassPathResource加载在类路径下的资源文件。再来一个案例。

ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");

FileSystemXmlApplicationContext将使用 URLResouce来加载文件系统中的配置文件,这里案例是相对路径。

当指定了资源前缀,则使用指定前缀对应的 Resource来加载资源。如下案例。

ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");

对比上一个案例,此处将使用 ClassPathResource加载在类路径下的资源文件。

指定单一的资源文件通过前面2种方式,若需要指定多个资源则可以考虑使用通配符,支持 Ant-styleclasspath*

出于向后兼容的历史原因, FileSystemXmlApplicationContext关联的 FileSystemResource会将所有的资源的非前缀路径统一当做相对路径。上案例

// 2种写法是一样的
ApplicationContext ctx =new FileSystemXmlApplicationContext("conf/context.xml");
ApplicationContext ctx =new FileSystemXmlApplicationContext("/conf/context.xml");
// 2种写法是一样的
ctx.getResource("some/resource/path/myTemplate.txt");
ctx.getResource("/some/resource/path/myTemplate.txt");
// 可以强制不按相对路径处理不,可以的!按URLResource处理
ctx = new FileSystemXmlApplicationContext("file:///conf/context.xml");

本文主要详细说明了

这一篇下来,基本Spring关于资源处理和相关接口总体是比较清晰了。

知识分享,转载请注明出处。学无先后,达者为先!

Original: https://www.cnblogs.com/kongbubihai/p/15915434.html
Author: kongxubihai
Title: Spring系列18:Resource接口及内置实现

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/572046/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Elasticsearch必备原理理解

    Elasticsearch读写原理 心得: 主分片、副本分片的存在类似各大组件的”主从结构”,需要注意的是,Elasticsearch的写入是针对 主分片,…

    Java 2023年6月6日
    084
  • 第一次的ssm整合

    数据库表 导入依赖 javax.servlet javax.servlet-api 4.0.1 provided org.junit.jupiter junit-jupiter-a…

    Java 2023年6月9日
    0156
  • 【干货】MySQL底层架构设计,你了解多少?

    很多开发同学对SQL优化如数家珍,却对MySQL架构一知半解。岂不是只见树叶,不见森林,终将陷入细节中不能自拔。 今天就一块学习MySQL分层架构,深入了解MySQL底层实现原理,…

    Java 2023年6月8日
    081
  • leetcode 538. Convert BST to Greater Tree 把二叉搜索树转换为累加树(简单)

    一、题目大意 给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node…

    Java 2023年6月14日
    068
  • 复杂类型注入

    java;gutter:true; /</p> <pre><code> ;gutter:true;/java;gutter:true; valu…

    Java 2023年6月13日
    069
  • Kubernetes集群环境搭建全过程

    资源准备以及服务器初始化 所有服务器执行一下脚本进行配置信息初始化: #!/bin/bash cd dirname $0 关闭selinux setenforce 0 sed -i…

    Java 2023年6月5日
    099
  • 高性能Java RPC框架Dubbo

    摘自《Java微服务分布式架构企业实战》 讲解了使用Spring Cloud来解决微服务应用程序开发过程中所遇到的一系列诸如客户端如何调用服务、服务与服务之间如何进行通信、服务如何…

    Java 2023年5月29日
    073
  • 头秃了,二十三张图带你从源码了解Spring Boot 的启动流程~

    前言 源码版本 从哪入手? 源码如何切分? 如何创建SpringApplication? 设置应用类型 设置初始化器(Initializer) 设置监听器(Listener) 设置…

    Java 2023年6月14日
    091
  • 单机高并发模型设计

    背景 在微服务架构下,我们习惯使用多机器、分布式存储、缓存去支持一个高并发的请求模型,而忽略了单机高并发模型是如何工作的。这篇文章通过解构客户端与服务端的建立连接和数据传输过程,阐…

    Java 2023年6月8日
    097
  • 如何设置博客首页显示透明的人物图像

    在个人博客的设置里,页脚HTML代码内输入以下内容: __ 按照此设置后,页面效果如图所示: 欢迎关注我的博客,获取更多精品知识合集 如果觉得对您有帮助的话,请帮我点赞、分享!您的…

    Java 2023年6月5日
    071
  • kafka_2.12-2.2.1 集群搭建

    一、zookeeper集群搭建 kafka集群依赖于zookeeper的集群,搭建zookeeper集群的步骤参考我之前写过的,Solr集群搭建详细教程(一)中的第二步 二、下载解…

    Java 2023年6月9日
    083
  • Spring JDBC

    用过JDBC(Java DataBase Connectivity,Java数据库连接)的人都知道,JDBC非常臃肿,一点也不可爱。以致于我们每次使用JDBC操作数据库时,总会忍不…

    Java 2023年6月5日
    080
  • 【设计模式】Java设计模式-装饰者模式

    Java设计模式 – 装饰者模式 😄 不断学习才是王道🔥 继续踏上学习之路,学之分享笔记👊 总有一天我也能像各位大佬一样🏆原创作品,更多关注我CSDN: 一个有梦有戏的…

    Java 2023年6月16日
    0110
  • List 移除元素 报错 java.lang.UnsupportedOperationException

    异常信息:java.lang.UnsupportedOperationExceptionat java.util.AbstractList.remove(AbstractList….

    Java 2023年6月5日
    093
  • SpringBootApplication启动排除DataSourceAutoConfiguration不生效???

    &#x3000;&#x3000;&#x9879;&#x76EE;&#x5F15;&#x7528;&#x4E86;&#…

    Java 2023年6月7日
    098
  • Java基础之变量、常量、作用域

    变量就是可以变化的量 Java是一种强类型语言,每个变量都必须声明其类型 Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域 注意事项: 每个变量都有类型,类…

    Java 2023年6月8日
    084
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球