Spring系列18:Resource接口及内置实现
本文内容
Resource接口的定义Resource接口的内置实现ResourceLoader接口ResourceLoaderAware接口
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 来完成它的的功能。具体看下一节的内置实现。
Resource接口的内置实现
UrlResourceClassPathResourceFileSystemResourceServletContextResourceInputStreamResourceByteArrayResource
UrlResource
UrlResource 包装了 java.net.URL,可用于访问通常可通过 URL 访问的任何对象,例如文件、HTTP 目标、FTP 目标等。这些资源通过标准前缀来区分,file:用于访问文件系统路径,http:用于通过 HTTP 协议访问资源,ftp:用于通过 FTP 访问资源等。
ClassPathResource
ClassPathResource表示应从类路径获取的资源。它使用线程上下文类加载器、给定的类加载器或给定的类来加载资源。可通过特定前缀classpath:指定为该资源。
如果类路径资源驻留在文件系统中,则此 Resource 实现支持解析为 java.io.File,但不支持在 jar 包中且尚未解压的类路径资源。
FileSystemResource
这是 java.io.File 和 java.nio.file.Path 句柄的资源实现。它支持作为File和 URL 的解析。
ServletContextResource
这是 ServletContext 资源的 Resource 实现,它解释相关 Web 应用程序根目录中的相对路径。
它始终支持流访问和 URL 访问,但仅在扩展 Web 应用程序存档并且资源物理位于文件系统上时才允许 java.io.File 访问。它是否被解压并在文件系统上或直接从 JAR 或其他地方(如数据库)访问实际上取决于 Servlet 容器。
ByteArrayResource
给定字节数组的资源实现。它为给定的字节数组创建一个 ByteArrayInputStream。对于从任何给定的字节数组加载内容很有用,而不必求助于单一使用的 InputStreamResource。
InputStreamResource
InputStreamResource 是给定 InputStream 的资源实现。首选 ByteArrayResource 或任何基于文件的 Resource 实现,仅当没有特定的 Resource 实现适用时才应使用它。
与其他 Resource 实现相比,这是一个已打开资源的描述符,接口isOpen()返回true。如果需要将资源描述符保存在某处或需要多次读取流,请不要使用它。
ResourceLoader接口
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");
如ctx是ClassPathXmlApplicationContext返回ClassPathResource;
如ctx是FileSystemXmlApplicationContext返回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: | classpath:com/myapp/config.xml |
从类路径加载 |
| file: | `file:///data/config.xml | 从文件系统作为 URL 加载 |
| http: | https://myserver/logo.png |
从文件系统作为 URL 加载 |
| (none) | /data/config.xml |
取决于底层的 ApplicationContext |
ResourceLoaderAware 接口
ResourceLoaderAware 接口是一个特殊的回调接口,它标识期望提供 ResourceLoader 引用的组件。其定义如下。
package org.springframework.context;
public interface ResourceLoaderAware extends Aware {
// 可自定义保存一个ResourceLoader的引用来进行资源加载
void setResourceLoader(ResourceLoader resourceLoader);
}
由于 org.springframework.context.ApplicationContext实现了 ResourceLoader,因此 bean 还可以实现 ApplicationContextAware 接口并直接使用提供的应用程序上下文来加载资源。从面向接口编程和解耦的角度来说,需要ResourceLoader的来进行资源加载,更推荐实现``ResourceLoaderAware 接口。
深一层思考,如果容器中的一个bean需要一个ResourceLoader依赖用于加载资源,除了实现ResourceLoaderAware,是否还有其它方式呢?
ResourceLoader实例会自动注入到IoC容器,我们可通过构造函数、setter方法、@Autowire注解等方式,直接注入该依赖,具体看一看之前Ioc相关的文章。
配置文件当做Resource 注入到bean依赖中
如果某个bean依赖于特定的Resource,那么我们如何快速优雅地注入该依赖呢?直接上代码。
public class MyConfig {
/**
* 通过配置文件注入的资源
*/
private Resource template;
public Resource getTemplate() {
return template;
}
public void setTemplate(Resource template) {
this.template = templa
}
}
MyConfig依赖一个类文件路径下的一个配置文件。
对应的xml文件spring.xml和配置文db.properties件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myConfig" class="com.crab.spring.ioc.demo02.MyConfig">
<property name="template" value="db.properties"></property>
</bean>
</beans>
url=jdbc:mysql://localhost/test
username=root
password=456
max=1024
运行主程序如下:
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);
}
}
运行结果:
class path resource [db.properties]
true
class path resource [db.properties]
- 成功注入了
Resourece资源到MyConfig的bean中 - 注入
Resource实际是我们通过ClassPathXmlApplicationContext加载的ClassPathResource
扩展:结合上一篇的资源特定前缀和ApplicationContext的关系,忘了请翻看下,我们也可以指定前缀来加载特定的资源。较为简单就不演示了。
<bean id="myConfig" class="com.crab.spring.ioc.demo02.MyConfig">
<property name="template" value="classpath:db.properties"></property>
</bean>file:///some/resource/path
<bean id="myConfig" class="com.crab.spring.ioc.demo02.MyConfig">
<property name="template" value="file:///some/resource/path"></property>
</bean>
// 注入的是FileSystemResource
使用资源路径配置应用上下文
回顾一下,ResourceLoader章节,我们分析了指定资源路径前缀对于资源加载方式的影响,对应关系如下表。下面将分带前缀和不带前缀的方式来配置应用上下文。
| 前缀 | 例子 | 如何解析 |
|---|---|---|
| classpath: | classpath:com/myapp/config.xml |
从类路径加载 |
| file: | `file:///data/config.xml | 从文件系统作为 URL 加载 |
| http: | https://myserver/logo.png |
从文件系统作为 URL 加载 |
| (none) | /data/config.xml |
取决于底层的 ApplicationContext |
资源路径不带前缀
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-style和classpath*。
Ant-style通配符/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/**/applicationContext.xml
当使用
Ant-style通配符,解析器遵循更复杂的过程来尝试解析通配符。它为直到最后一个非通配符段的路径生成一个资源,并从中获取一个URL。它为直到最后一个非通配符段的路径生成一个资源,并从中获取一个URL。最后一个非通配符段的路径分2种情况处理:
普通的文件路径: 生成
java.io.File,如classpath:com/mycompany/**/applicationContext.xml取到classpath:com/mycompany,然后进行遍历。特殊的文件
jar:路径:如classpath:com/mycompany/**/applicationContext.xml,取到classpath:com/mycompany,将生成java.net.JarURLConnection,或是手动解析 jarURL然后遍历 jar 文件的内容来解析通配符。使用 jar
URL强烈建议测试下是否能正常通过通配符访问到资源
classpath*前缀如下案例
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
遍历类路径下所有匹配到
conf/appContext.xml的资源都会加载。两种方式可以组合使用
如
classpath*:META-INF/*-beans.xml解决策略相当简单:在最后一个非通配符路径段上使用 ClassLoader.getResources() 调用来获取类加载器层次结构中的所有匹配资源,然后对于每个资源,
PathMatcher解析策略用于通配符子路径。感兴趣的可以了解下PathMatcher类。组合使用容易掉坑的2个地方,请注意:
classpath*:*.xml可能不会从 类路径下的jar 文件的根目录中检索文件,而只能从扩展目录的根目录中检索文件。
classpath:com/mycompany/**/service-context.xml如果要搜索的根包在多个类路径位置中可用,则不保证资源能够找到匹配的资源。 使用
ClassLoader#getResource(com/mycompany/)如果多个路径有,则可能只返回第一个,其它的漏掉了。保险的做法是classpath*:com/mycompany/**/service-context.xml
彩蛋
出于向后兼容的历史原因, 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");
总结
本文主要详细说明了
Resource接口的定义和方法说明Resource接口的内置6种实现ResourceLoader接口和实现ResourceLoaderAware接口使用- 如何使用
Resource配置ApplicationContext
这一篇下来,基本Spring关于资源处理和相关接口总体是比较清晰了。
知识分享,转载请注明出处。学无先后,达者为先!
Spring系列18:Resource接口及内置实现的更多相关文章
- 流程自动化RPA,Power Automate Desktop系列 - 不讲武德的Windows10内置应用
简介 Power Automate Desktop 扩展Power Automate中的现有机器人流程自动化(RPA)功能,并使您能够自动化所有重复的桌面流程. 使用预生成的拖放操作或记录您自己的桌面 ...
- Spring Cloud Gateway实战之五:内置filter
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- 【Android】18.1 利用安卓内置的定位服务实现位置跟踪
分类:C#.Android.VS2015: 创建日期:2016-03-04 一.安卓内置的定位服务简介 通常将各种不同的定位技术称为位置服务或定位服务.这种服务是通过电信运营商的无线电通信网络(如GS ...
- Spring Boot 添加jersey-mvc-freemarker依赖后内置tomcat启动不了解决方案
我在我的Spring Boot 项目的pom.xml中添加了jersey-mvc-freemarker依赖后,内置tomcat启动不了. 报错信息如下: org.springframework.con ...
- .net core系列之《.net core内置IOC容器ServiceCollection》
一.IOC介绍 IOC:全名(Inversion of Control)-控制反转 IOC意味着我们将对象的创建控制权交给了外部容器,我们不管它是如何创建的,我们只需要知道,当我们想要某个实例时,我们 ...
- Spring Boot2.0之 原理—创建内置Tomcat容器
前面所述的https://www.cnblogs.com/toov5/p/9823728.html 中的第一条先不赘述了,就是玩了maven 重点介绍后两条 首先内置Tomcat: SpringBoo ...
- Spring Cloud Gateway实战之四:内置predicate小结
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- python学习 day12 (3月18日)----(装饰器内置函数)
读时间函数: # import time # def func(): # start_time = time.time() # 代码运行之前的时间 # print('这是一个func函数') # ti ...
- $python正则表达式系列(3)——正则内置属性
本文主要总结一下python正则的一些内置属性的用法. 1. 编译标志:flags 首先来看一下re.findall函数的函数原型: import re print('[Output]') print ...
随机推荐
- Go语言系列之标准库flag
Go语言内置的flag包实现了命令行参数的解析,flag包使得开发命令行工具更为简单. os.Args 如果你只是简单的想要获取命令行参数,可以像下面的代码示例一样使用os.Args来获取命令行参数. ...
- JSP页面实际上就是Servlet
注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6513069824690618883/ 前面一直提到了Servlet的内容,也是我们平时理解的后台,这次说一下前台的 ...
- SSM工程常见问题
1.引入Junit测试 <!--引入junit单元测试,两个一起引用,spring-test版本要与Spring-core保持一致--> <dependency> <gr ...
- 安霸pipeline简述之YUV域的处理
YUV域处理模块的详细介绍: YUV域的处理主要是rgb_to_yuv_matrix,chroma_scale,ASF(空域降噪),MCTF(时域降噪),SharpenB(锐化模块). RGB2YUV ...
- 在pyqt5中展示pyecharts生成的图像
技术背景 虽然现在很少有人用python去做一些图形化的界面,但是不得不说我们在日常大部分的软件使用中都还是有可视化与交互这样的需求的.因此pyqt5作为一个主流的python的GUI框架地位是非常重 ...
- Linux命令(2)--cp拷贝、mv剪切、head、tail追踪、tar归档
文章目录 一.知识回顾 ls cd 二.Linux基本操作(二) 1.cp 拷贝 2.mv 移动(剪切) 3.head 头部 4.tail 追踪(尾部) 5.tar 归档 查看 压缩 解压 总结 一. ...
- [Jetson Nano]Jetson Nano快速入门
NVIDIAJetsonNano开发套件是适用于制造商,学习者和开发人员的小型AI计算机.相比Jetson其他系列的开发板,官方报价只要99美金,可谓是相当有性价比.本文如何是一个快速入门的教程,主要 ...
- CentOS 8安装Docker报错(Problem: package docker-ce-3:19.03.8-3.el7.x86_64 requires containerd.io >= 1.2.)
CentOS8安装docker和docer-conpose 报错如下Problem: package docker-ce-3:19.03.8-3.el7.x86_64 requires contain ...
- AOP-底层原理
AOP(底层原理) 1,AOP底层使用动态代理 (1)有两种情况动态代理 第一种 有接口情况,使用JDK动态代理 *创建接口实现类代理对象,增强类的方法 第二种 无接口情况,使用CGLIB动态代理 * ...
- Typora下载安装教程(全面)
Typora下载与安装 一:下载 1.1百度搜索 https://www.typora.io/ 点击链接进入后映入眼帘的就是一款简洁的Typora网页,然后下滑进入主页. 1.2点击Download( ...