Spring Resource和ResourceLoader源码解析
Spring用Resource接口抽象所有的底层资源,包括File、ClassPath、URL等。ResourceLoader接口是Resource的加载器,根据资源的路径/路径模式获取Resource实例。
Resource
接口定义
Resource接口的定义如下:
public interface Resource extends InputStreamSource {boolean exists();default boolean isReadable() {return exists();}default boolean isOpen() {return false;}default boolean isFile() {return false;}URL getURL() throws IOException;URI getURI() throws IOException;File getFile() throws IOException;default ReadableByteChannel readableChannel() throws IOException {return Channels.newChannel(getInputStream());}long contentLength() throws IOException;long lastModified() throws IOException;Resource createRelative(String relativePath) throws IOException;@NullableString getFilename();String getDescription();}
它继承自InputStreamSource接口,这个接口只有一个方法:
public interface InputStreamSource {InputStream getInputStream() throws IOException;}
这里比较重要的几个方法有:
- getInputStream(),定位并且打开当前资源,返回该资源的一个InputStream。每次调用都会返回一个新的InputStream。
- exists(),返回该资源是否物理存在。
- getURL(),返回该资源的URL句柄。
- getURI(),返回该资源的URI句柄。
- isFile(),是否是文件。
- getFile(),返回该资源的File句柄。
Resource接口的实现
Spring提供了多种Resource实现,我们可以直接使用。
FileSystemResource
FileSystemResource是针对java.io.File的Resource实现类,其构造函数与getInputStream()方法如下:
public class FileSystemResource extends AbstractResource implements WritableResource {private final String path;@Nullableprivate final File file;private final Path filePath;public FileSystemResource(String path) {Assert.notNull(path, "Path must not be null");this.path = StringUtils.cleanPath(path);this.file = new File(path);this.filePath = this.file.toPath();}//other constructors......@Overridepublic InputStream getInputStream() throws IOException {try {return Files.newInputStream(this.filePath);}catch (NoSuchFileException ex) {throw new FileNotFoundException(ex.getMessage());}}...
}
可以看到它封装了java.io.File,在获取输入流时就是打开了该文件的NIO文件流。
ClassPathResource
ClassPathResource是类路径下资源的Resource实现。它通过ClassLoader或Class来加载资源。其构造函数和getInputStream()方法的实现如下:
public class ClassPathResource extends AbstractFileResolvingResource {private final String path;@Nullableprivate ClassLoader classLoader;@Nullableprivate Class<?> clazz;public ClassPathResource(String path, @Nullable 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());}//other constructors...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;}...}
可以看到它最终委托Class对象或ClassLoader对象来加载资源。如果创建该ClassPathResource时指定了用于加载资源的Class对象,它会通过该Class对象的getResourceAsStream来加载资源;否则,如果指定了ClassLoader,它会通过该ClassLoader对象的getResourceAsStream来加载资源;否则,通过系统ClassLoader来加载资源。
UrlResource
UrlResource是java.net.URL的Resource实现类,用来访问URL可以正常访问的任意对象。其内部封装了URL对象,因此,它支持http、https、file、ftp、jar等协议。其getInputStream()方法实现如下:
public InputStream getInputStream() throws IOException {URLConnection con = this.url.openConnection();ResourceUtils.useCachesIfNecessary(con);try {return con.getInputStream();}catch (IOException ex) {// Close the HTTP connection (if applicable).if (con instanceof HttpURLConnection) {((HttpURLConnection) con).disconnect();}throw ex;}
}
可以看到它是打开一个新的到该URL所引用的远程对象的连接,获取URLConnection对象,然后获取输入流。
InputStreamResource
InputStreamResource把一个InputStream封装为Resource,其内部实现大致如下:
public class InputStreamResource extends AbstractResource {private final InputStream inputStream;private final String description;private boolean read = false;public InputStreamResource(InputStream inputStream, @Nullable String description) {Assert.notNull(inputStream, "InputStream must not be null");this.inputStream = inputStream;this.description = (description != null ? description : "");}...@Overridepublic InputStream getInputStream() throws IOException, IllegalStateException {if (this.read) {throw new IllegalStateException("InputStream has already been read - " +"do not use InputStreamResource if a stream needs to be read multiple times");}this.read = true;return this.inputStream;}...
}
ByteArrayResource
ByteArrayResource把字节数组封装为Resource。其实现大致如下:
public class ByteArrayResource extends AbstractResource {private final byte[] byteArray;private final String description;public ByteArrayResource(byte[] byteArray, @Nullable String description) {Assert.notNull(byteArray, "Byte array must not be null");this.byteArray = byteArray;this.description = (description != null ? description : "");}...@Overridepublic InputStream getInputStream() throws IOException {return new ByteArrayInputStream(this.byteArray);}...
}
ResourceLoader
接口定义
ResourceLoader的接口定义如下:
public interface ResourceLoader {Resource getResource(String location);@NullableClassLoader getClassLoader();
}
其中,getResource(String)接口返回指定位置的资源,getClassLoader()接口返回该ResourceLoader所使用的ClassLoader。
ResourceLoader接口的实现
ApplicationContext
Spring应用上下文都实现了ResourceLoader接口,因此所有的应用上下文都可以通过getResource(String)方法获取Resource实例。
DefaultResourceLoader
DefaultResourceLoader是默认的ResourceLoader实现,同时它也是AbstractApplicationContext类的基类。它的实现大致如下:
public class DefaultResourceLoader implements ResourceLoader {@Nullableprivate ClassLoader classLoader;private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);private final Map<Class<?>, Map<Resource, ?>> resourceCaches = new ConcurrentHashMap<>(4);...public DefaultResourceLoader(@Nullable ClassLoader classLoader) {this.classLoader = classLoader;}...@Override@Nullablepublic ClassLoader getClassLoader() {return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());}public void addProtocolResolver(ProtocolResolver resolver) {Assert.notNull(resolver, "ProtocolResolver must not be null");this.protocolResolvers.add(resolver);}public <T> Map<Resource, T> getResourceCache(Class<T> valueType) {return (Map<Resource, T>) this.resourceCaches.computeIfAbsent(valueType, key -> new ConcurrentHashMap<>());}@Overridepublic Resource getResource(String location) {Assert.notNull(location, "Location must not be null");for (ProtocolResolver protocolResolver : this.protocolResolvers) {Resource resource = protocolResolver.resolve(location, this);if (resource != null) {return resource;}}if (location.startsWith("/")) {return getResourceByPath(location);}else if (location.startsWith(CLASSPATH_URL_PREFIX)) {return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());}else {try {// Try to parse the location as a URL...URL url = new URL(location);return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));}catch (MalformedURLException ex) {// No URL -> resolve as resource path.return getResourceByPath(location);}}}...
}
可以看到,在创建DefaultResourceLoader的时候可以指定类加载器,如果没有指定,就会使用默认的类加载器。
同时,该类维护了一个ProtocolResolver集合,用来处理其他协议。ProtocolResolver是一个接口,其源码如下:
@FunctionalInterface
public interface ProtocolResolver {@NullableResource resolve(String location, ResourceLoader resourceLoader);}
这是一个函数接口,其唯一的抽象方法resolve,第一个参数是资源路径,第二个参数是针对该类协议的资源的资源加载器。
然后我们看一下DefaultResourceLoader的getResource(String)方法,该方法首先通过用户自己注册的ProtocolResolver尝试解析和加载资源,如果成功则直接返回;若不成功,则判断资源路径是否以/开头,如果以/开头,则直接返回一个ClassPathContextResource,若不是以/开头,但是以classpath:开头,则直接返回一个ClassPathResource,否则,根据协议返回FileUrlResource或UrlResource。
ResourcePatternResolver
ResourcePatternResolver是继承自ResourceLoader接口的一个接口,用来解析路径模式,例如Ant风格的路径模式,其源码如下:
public interface ResourcePatternResolver extends ResourceLoader {String CLASSPATH_ALL_URL_PREFIX = "classpath*:";Resource[] getResources(String locationPattern) throws IOException;
}
其中,getResources(String)接口根据输入的路径模式,返回Resource数组。
ResourcePatternResolver的一个实现是PathMatchingResourcePatternResolver,它可以解析指定的资源位置路径到一个或多个匹配的Resource,这里,资源路径可以是一个指向某一个特定的资源的简单路径,也可以是包含classpath*:前缀的路径,路径里还可以使用Ant风格正则表达式。例如,你可以使用以下这些路径:
- file:C:/context.xml;
- classpath:/context.xml;
- /WEB-INF/context.xml;
- /WEB-INF/*-context.xml;
- file:C:/some/path/*-context.xml;
- com/mycompany/**/applicationContext.xml
- classpath:com/mycompany/**/applicationContext.xml
- classpath*:META-INF/*-beans.xml
Ant风格正则表达式的细节可以参见这篇文章:https://jinnianshilongnian.iteye.com/blog/1416322。
Spring Resource和ResourceLoader源码解析相关推荐
- spring 注解试事物源码解析
spring 注解试事物源码解析 基于xml注解式事务入口 public class TxNamespaceHandler extends NamespaceHandlerSupport {stati ...
- Spring事件机制Event源码解析(未完待续)
Spring事件机制Event源码解析(未完待续) 监听器: ApplicationEvent事件 ApplicationListener监听器(观察者) ApplicationEventMultic ...
- Spring AOP 超详细源码解析
知识章节 基础知识 什么是 AOP AOP 的全称是 "Aspect Oriented Programming",即面向切面编程 在 AOP 的思想里面,周边功能(比如性能统计,日 ...
- Spring之循环依赖源码解析
目录 1.什么是循环依赖? 2.为什么会出现循环依赖? 3.面对循环依赖问题,我们该如何思考解决? 4.Spring是怎么解决循环依赖的? 5.总结 1.什么是循环依赖? 有两个类Order.Cust ...
- 【Spring Boot实战】源码解析Spring Boot自动配置原理
一.简介 Spring致力于让Java开发更简单,SpringBoot致力于让使用Spring进行Java开发更简单,SpringCloud致力于基于SpringBoot构建微服务生态圈,让微服务开发 ...
- Spring之依赖注入源码解析
依赖注入底层原理流程图:Spring中Bean的依赖注入原理 | ProcessOn免费在线作图,在线流程图,在线思维导图 | 1.Spring中到底有几种依赖注入方式 手动注入和自动注入 1.手动注 ...
- Spring aware使用与源码解析
目录 1. aware接口的作用 2. 常用aware接口及作用 3. 使用样例:ApplicationContextAware 在Bean中获取上下文 4. 自定义aware的方式 4.1 定义继承 ...
- spring 启动之全过程 源码解析
主类代码 public class BeanLifeCycle {public static void main(String[] args) {System.out.println("现在 ...
- 死磕Spring系列:SpringAOP源码解析
1. 术语 在SpringAOP中,有很多专业术语,已经了解的小伙伴可以略过,不了解的朋友就需要注意看一下啦,不然会很懵的哟. Aspect(切面) 跨多个类的关注点的模块化,与垂直调用的业务树不同, ...
最新文章
- 写出一个超强的lighttpd模块
- VMware workstation安装
- LARS 最小角回归算法简介
- 页面加载完后立刻执行JS的两种方法
- php 去除 html 属性,用PHP 去掉所有html标签里的部分属性
- linux下mysql5.7修改密码
- XP无法建立宽带连接的解决方法
- rust睡觉按键没反应_腐蚀Rust有哪些实用操作 腐蚀Rust实用操作汇总-游侠网
- Java完全自学手册,从外包到大厂,再到年薪100万都靠它
- 服务器IP被封怎么办
- 电话机上面的接头RJ11
- WebStorm下载其他版本(历史版本)
- java backoff_Java BackOff类代码示例
- AD(Altium Designer)导出BOM时出错处理
- service两种启动方式的区别
- S3C2440系统中断(转)
- DVB-subtitle解析流程浅
- c语言char s[] 语句,35、若有定义和语句: char s[10]=abcd;printf(%s\n,s); 则结果是(以下u代表空格)...
- HTTPS原理解析-转
- Web应用架构搭建漏洞概念HTTP
热门文章
- 别停特斯拉旁边!特斯拉车辆自燃全车烧毁 连旁边的奥迪都没放过...
- 提高django model效率的几个小方法
- linux pxe安装视频,Linux—图解PXE实现全自动安装系统(1)
- python比java难吗-Python 的开发效率真的比 Java高吗?
- rtmp服务器 协议之同步
- 看门狗超时前在内核打印信息
- html 换行_李亚涛:清除HTML所有格式并且删除换行与回车,只显示文本
- python中合法的二进制整数_python:求整数的二进制表示
- 【Spring】Bean instantiation via constructor failed nested exception Constructor threw exception
- 95-18-015-配置-AbstractBootstrapConfig