05.Spring 资源加载 - Resource
基本概念
Spring 把所有能记录信息的载体,如各种类型的文件、二进制流等都称为资源。
对 Spring 开发者来说,最常用的资源就是 Spring 配置文件(通常是一份 XML 格式的文件)。
Spring 为资源访问提供了一个 Resource 接口,利用该接口来表示不同类型的资源。并且 Spring 框架本身大量使用了 Resource 接口来访问底层资源。
Resource 接口是具体资源访问策略的抽象,也是所有资源访问类所实现的接口。
内部构造
Resource 接口继承了 InputStreamSource 接口,通过该接口,可以将任意形式的资源转成流来操作。
下面来看它的源码:
- InputStreamSource
public interface InputStreamSource {
// 表示任意形式的资源都可以被转换成输入流
InputStream getInputStream() throws IOException;
}
- Resoure
public interface Resource extends InputStreamSource {
// 资源是否存在,true 表示存在
boolean exists();
// 资源是否可读,true 表示存在
boolean isReadable();
// 资源是否打开,true 表示资源只能被读取一次然后关闭以避免资源泄露
boolean isOpen();
// 当前资源能由 java.util.URL 代表时,则返回该 URL,否则抛出异常
URL getURL() throws IOException;
// 当前资源能由 java.util.URI 代表时,则返回该 URI,否则抛出异常
URI getURI() throws IOException;
// 当前资源能由 java.io.File 代表时,则返回该 File,否则抛出异常
File getFile() throws IOException;
// 资源的长度,一般是值代表的文件资源的长度
long contentLength() throws IOException;
// 资源的最后修改时间
long lastModified() throws IOException;
// 创建相对于当前资源代表的资源
// 比如当前资源代表文件资源 [d:/test/] 则 [createRelative("test.txt")] 将返回文件资源 [d:/test/test.txt]
Resource createRelative(String relativePath) throws IOException;
// 资源的文件路径,比如 File 资源就返回它的文件路径,URL 资源则返回空
String getFilename();
// 资源的描述符,通常就是资源的全路径(实际文件名或实际URL地址)。
String getDescription();
}
Resource 接口是 Spring 资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略。
在 Spring 中存在这大量的 Resource 实现类来表示不同形式的资源。具体如下图所示:
- 继承关系
- 常见的资源类型
资源类型
Resource 实现类针对不同的的底层资源,提供了相应的资源访问逻辑,并提供便捷的包装,以利于客户端程序的资源访问。这其实也是典型的策略模式。下面来看几种常见的资源类型以及对应的访问方法。
1.ByteArrayResource
访问字节数组资源的实现类。它在其内部维护着一个字节数组,并该资源一旦创建就表示其一定存在,因此 exists 方法默认返回 true。我们可以通过将字节数组转换成 ByteArrayInputStream 来实现对它的访问。具体源码如下:
public class ByteArrayResource extends AbstractResource {
// 字节数组
private final byte[] byteArray;
// 描述,可由开发者自定义,为空时则为默认值
private final String description;
// 构造函数
public ByteArrayResource(byte[] byteArray) {
this(byteArray, "resource loaded from byte array");
}
// 构造函数
public ByteArrayResource(byte[] byteArray, String description) {
if (byteArray == null) {
// 抛出异常
}
this.byteArray = byteArray;
this.description = (description != null ? description : "");
}
// 关键 -> 将数组转换成 ByteArrayInputStream
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(this.byteArray);
}
// 关键 -> 默认返回 true,表示该资源一旦被创建就默认存在
public boolean exists() {
return true;
}
//省略部分源码...
}
接着来看如何使用 ByteArrayResource 访问字节数组:
// 定义流读取方法
public static void readResource(Resource resource) throws IOException{
InputStream is = resource.getInputStream();
byte[ ] buf = new byte[1024];
int len =0;
while((len=is.read(buf)) !=-1){
System.out.println(new String(buf,0,len) );
}
}
public static void main(String [ ] args) throws IOException {
Resource resouce = new ByteArrayResource("hello".getBytes());
readResource(resouce);
}
2.InputStreamResource
访问输入流资源的实现类。在其内部维护着一个输入流,因此我们可以直接取得该输入流并对其进行访问。值得注意的是,该输入流只允许读取一次(对应它的 isOpen 方法),若再次读取则会抛出异常。
public class InputStreamResource extends AbstractResource {
// 字符输入流
private final InputStream inputStream;
// 描述
private final String description;
// 表示是否可读
private boolean read = false;
public InputStreamResource(InputStream inputStream) {
this(inputStream, "resource loaded through InputStream");
}
public InputStreamResource(InputStream inputStream, String description) {
if (inputStream == null) {
// 抛出异常...
}
this.inputStream = inputStream;
this.description = (description != null ? description : "");
}
// 关键--> 直接取得输入流(本身就是流,无需再转换)
public InputStream getInputStream() throws IOException, IllegalStateException {
// 表示只能读取一次,对应 isOpen
if (this.read) {
// 抛出异常...
}
this.read = true;
return this.inputStream;
}
// 为 ture 表示只能读取一次
public boolean isOpen() {
return true;
}
// 关键 -> 默认返回 ture
public boolean exists() {
return true;
}
}
接着来看如何使用 InputStreamResource 访问字符输入流:
// 用 bis 表示输入流
ByteArrayInputStream bis = new ByteArrayInputStream("hello".getBytes());
InputStreamResource resource = new InputStreamResource(bis);
readByteArrayResource(resource);
// 关键-> 再次读取资源,抛出异常
readResource(resource);
3.FileSystemResource
访问文件系统里资源的实现类。该类访问的资源为 java.io.File,即文件对象。它通过 file 的路径找到指定的文件。并且可以将其转成 FileInputStream 来操作。
isOpen 方法继承自父类 AbstractResource ,返回 false,表示该资源可以被多次读取。
public class FileSystemResource extends AbstractResource implements WritableResource {
// 文件
private final File file;
// 文件路径
private final String path;
public FileSystemResource(File file) {
Assert.notNull(file, "File must not be null");
this.file = file;
this.path = StringUtils.cleanPath(file.getPath());
}
public FileSystemResource(String path) {
Assert.notNull(path, "Path must not be null");
this.file = new File(path);
this.path = StringUtils.cleanPath(path);
}
// 关键 -> 根据 file 来判断,如果指定路径不存该文件,则返回 false
public boolean exists() {
return this.file.exists();
}
// 是否可读
public boolean isReadable() {
return (this.file.canRead() && !this.file.isDirectory());
}
// 关键 -> 取得 FileInputStream
public InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}
// 关键 -> 创建相相对于当前资源路径下的资源。
// 假设当前的 file 路径为 D:/DEMO/HELLO.TXT,且 relativePath = GOOD.TXT
// 则该方法创建的资源路径为 D:/DEMO/GOOD.TXT
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
return new FileSystemResource(pathToUse);
}
}
接着来看如何使用 FileSystemResource 访问文件对象:
Resource resource = new FileSystemResource("D:/DEMO/HELLO.txt");
// 关键 -> 表示读取 D:/DEMO/HELLO/GOOD.txt 路径下的文件
Resource resource2 = resource.createRelative("HELLO/GOOD.txt");
readResource(resource);
readResource(resource2);
4.ClassPathResource
用来访问类加载路径下的资源,相对于其他的 Resource 实现类,其主要优势是方便访问类加载路径里的资源,尤其对于 Web 应用,ClassPathResource 可自动搜索位于 WEB-INF/classes 下的资源文件,无须使用绝对路径访问。
public class ClassPathResource extends AbstractFileResolvingResource {
// 路径
private final String path;
// 类加载器
private ClassLoader classLoader;
// 类对象
private Class<?> clazz;
// 关键 ->使用默认的 ClassLoader 加载 path 类路径资源
public ClassPathResource(String path) {
this(path, (ClassLoader) null);
}
// 关键-> 使用指定的 ClassLoader 加载 path 类路径资源
public ClassPathResource(String path, ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
String pathToUse = StringUtils.cleanPath(path);
if (pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
this.path = pathToUse;
this.classLoader = ( classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader() );
}
// 关键-> 指定的类进行加载资源,将加载相对于当前类的路径的资源
public ClassPathResource(String path, Class<?> clazz) {
Assert.notNull(path, "Path must not be null");
this.path = StringUtils.cleanPath(path);
this.clazz = clazz;
}
protected ClassPathResource(String path, ClassLoader classLoader, Class<?> clazz) {
this.path = StringUtils.cleanPath(path);
this.classLoader = classLoader;
this.clazz = clazz;
}
// 获取类加载器
public final ClassLoader getClassLoader() {
return ( this.clazz != null ? this.clazz.getClassLoader() : this.classLoader );
}
// 资源是否存在
public boolean exists() {
return ( resolveURL() != null );
}
protected URL resolveURL() {
if (this.clazz != null) {
return this.clazz.getResource(this.path);
} else if (this.classLoader != null) {
return this.classLoader.getResource(this.path);
} else {
return ClassLoader.getSystemResource(this.path);
}
}
public InputStream getInputStream() throws IOException {
InputStream is;
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
} else if (this.classLoader != null) {
is = this.classLoader.getResourceAsStream(this.path);
} else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
return is;
}
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
return new ClassPathResource(pathToUse, this.classLoader, this.clazz);
}
}
接着来看如何使用 ClassPathResource 访问类加载路径下的资源:
public static void main(String [ ] args) throws IOException {
Test test = new Test;
test.readResource();
}
public void readResource() throws IOException{
// 假设该类的路径是 com\demo\,则该资源的路径为 com\demo\com\demo\hello.txt
Resource resource = new ClassPathResource("com\\demo\\hello.txt",this.getClass());
InputStream is = resource.getInputStream();
byte[ ] buf = new byte[1024];
int len =0;
while((len=is.read(buf)) !=-1){
System.out.println(new String(buf,0,len) );
}
is.close();
}
5.UrlResource
该资源代表 URL 资源,用于简化 URL 资源访问,且它可以被多次读取。
一般支持如下资源访问:
http:通过标准的http协议访问web资源,如new UrlResource(“http://地址”);
ftp:通过ftp协议访问资源,如new UrlResource(“ftp://地址”);
file:通过file协议访问本地文件系统资源,如new UrlResource(“file:d:/test.txt”);
6.ServletContextResource
该资源代表 web 应用资源,用于简化 Servlet 容器的 ServletContext 接口的 getResource 操作和 getResourceAsStream 操作。
参考
http://www.blogjava.net/DLevin/archive/2012/12/01/392325.html
https://www.ibm.com/developerworks/cn/java/j-lo-spring-resource/
05.Spring 资源加载 - Resource的更多相关文章
- spring资源加载结构解析
1.spring中资源加载使用resources的原因? 在java将不同资源抽象成url,然后通过注册不同的hander来处理不同读取逻辑,一般hander使用协议的前缀来命名,如http,jar, ...
- Spring资源加载器抽象和缺省实现 -- ResourceLoader + DefaultResourceLoader(摘)
概述 对于每一个底层资源,比如文件系统中的一个文件,classpath上的一个文件,或者一个以URL形式表示的网络资源,Spring 统一使用 Resource 接口进行了建模抽象,相应地,对于这些资 ...
- 06.Spring 资源加载 - ResourceLoader
基本概念 ResourceLoader 接口,在 Spring 中用于加载资源,通过它可以获取一个 Resouce 对象. 内部构造 首先来看它的接口定义: public interface Reso ...
- spring 资源加载使用说明
Spring 提供了一个强大加载资源的机制,不但能够通过“classpath:”.“file:” 等资源地址前缀识别不同的资源类型,还支持Ant 风格带通配符的资源地址. 首先,我们来了解一下Spri ...
- Spring资源加载基础ClassLoader
1 ClassLoader工作机制 1.1 ClassLoader作用 寻找类字节码文件并构造出类在JVM内部表示的组件.负责运行时查找和装入Class字节码文件 1.2 装载步骤 1.2.1 装载 ...
- Spring 资源加载
pom.xml ``` org.springframework spring-core 4.3.14.RELEASE org.springframework spring-beans 4.3.16.R ...
- 【死磕 Spring】----- IOC 之 Spring 统一资源加载策略
原文出自:http://cmsblogs.com 在学 Java SE 的时候我们学习了一个标准类 java.net.URL,该类在 Java SE 中的定位为统一资源定位器(Uniform Reso ...
- spring资源访问接口和资源加载接口
spring 资源访问接口 JDK提供的资源访问类,如java.net.URL.File等,不能很好地满足各种资源的访问需求,比如缺少从类路径或者Web容器的上下文中获取资源的操作类. 鉴于此,spr ...
- 简说Spring中的资源加载
声明: 本文若有 任何纰漏.错误,请不吝指正!谢谢! 问题描述 遇到一个关于资源加载的问题,因此简单的记录一下,对Spring资源加载也做一个记录. 问题起因是使用了@PropertySource来进 ...
随机推荐
- oracle 12c 多租户 pdb 恢复(单个pdb数据文件、非系统pdb表空间、整个pdb数据库)
环境:数据库版本 Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production 实验准备:1.-- 数据库归 ...
- [转]各种开源协议介绍 BSD、Apache Licence、GPL V2 、GPL V3 、LGPL、MIT
现今存在的开源协议很多,而经过Open Source Initiative组织通过批准的开源协议目前有58种(http://www.opensource.org/licenses /alphabeti ...
- nefu26(求数的位数)
Description 根据密码学需要,要计算某些数的阶乘的位数. Input 第一行为整数n ,接下来 n 行, 每行1个数m (1 ≤ m ≤ 10^7) . Output 输出m的阶乘的位数. ...
- HDOJ1213(并查集)
set容器中的值互异,非常好用. 水题,直接贴代码了 #include<iostream> #include<cstdio> #include<set> using ...
- 集群重启某一主机下所有osd down解决办法
标签(空格分隔): ceph 运维 osd 问题描述: 掉电后,上电发现cluster中的主机node3下的所有osd都down掉了,通过命令重启node3的ceph-osd服务,osd依然无法up: ...
- smbpasswd和pdbedit
samba用户管理: smbpasswd :smbpasswd命令属于samba套件,能够实现添加或删除samba用户和为用户修改密码. smbpasswd [options] USERNAME -a ...
- Python-通过socket实现一个小型的端口检测工具
实验机器IP:192.168.220.139,端口开放情况 代码 # -*- coding:utf-8 -*- __author__ = "MuT6 Sch01aR" import ...
- C#动态给EXCEL列添加下拉选项
Microsoft.Office.Interop.Excel.Application excel=new Microsoft.Office.Interop.Excel.Application(); M ...
- 问题:C# Dictionary嵌套使用;结果:嵌套Dictionary添加 , C#2.0泛型详细介绍
Dictionary<int, Dictionary<string, string>> dict1 = new Dictionary<int, Dictionary< ...
- 8.ireport 取消自动分页,detail不分页
转自:http://www.blogjava.net/vjame/archive/2013/10/12/404908.html 报表文件属性页面 lgnore pagination 勾选上,就可以取消 ...