Spring 源码分析——资源文件加载
本篇文章讨论 Spring 源码中如何加载和解析资源文件。
对应 Github 源码完整文档:https://github.com/TyCoding/mini-spring/tree/main/docs/ioc/04-resource-loader
之前我们提到,在 Spring 中 Bean 的生命周期第一步是需要定位加载,而这种定位方式有多种,其中从配置文件中加载 Bean 就是一种常见的方式,那么就涉及到 Spring 读取指定配置文件并加载到项目中。
在 Spring 中提供了Resource
接口用于处理项目运行时需要加载的任何资源,Resource 接口的主要实现类有如下:
-
UrlResource:表示通过 URL 定位资源,可以通过 HTTP URL 或者本地文件路由获取资源; -
ClassPathResource:表示通过 Classpath 定位资源,在 SpringBoot 项目中 classpath:
表示项目的resources
目录; -
FileSystemResource:表示通过文件路径定位资源,可以是项目的相对或绝对路径; -
ServletContextResource:表示通过 ServerContext 定位资源,在 JavaWeb 项目中表示 WEB-INF
目录;
其中,Resource 对文件流的处理提供了两个接口:
-
InputStreamResource:基于输入流的 Resource 实现接口,接受一个 InputStream 参数,用于将输入流封装成 Resource 对象; -
WriteableResource:可写的 Resource 实现接口,表示可以写入数据的资源,提供 write()
函数将数据写入指定数据流;
设计模式
Spring 对于 Resource 的设计实现中用到了很多设计模式;这里我们先按照上面的讲解设计几个实现类:
// ClassPath资源加载
public class ClassPathResource implements Resource {
private final String path;
public ClassPathResource(String path) {
this.path = path;
}
@Override
public InputStream getInputStream() throws IOException {
// 从ClassLoader拿到此path对应的输入流
}
}
// Url资源加载
public class UrlResource implements Resource {
private final URL url;
public UrlResource(URL url) {
this.url = url;
}
@Override
public InputStream getInputStream() throws IOException {
// URL对象中拿到网络连接输入流
}
}
// 系统资源加兹安
public class FileSystemResource implements Resource {
private final String filePath;
public FileSystemResource(String filePath) {
this.filePath = filePath;
}
@Override
public InputStream getInputStream() throws IOException {
// 从磁盘路径获取文件输入流
}
}
工厂模式
工厂模式:将对象创建过程抽象出来,定义一个抽象工厂接口,让具体的工厂子类负责对象的创建,在客户端和实现创建类之间建立一个间接层,避免客户端直接依赖具体的实现类。
简单说,工厂模式就是通过定义工厂接口,客户端调用工厂接口,工厂接口的实现交由子类负责。
在 Resource 接口的工厂模式具体体现是ResourceLoader
接口:
public interface ResourceLoader {
Resource getResource(String location);
}
ResourceLoader
接口定义了获取资源的方法,而具体获取资源的实现交由Resource
接口的实现类实现。也就是只需要调用 ResourceLoader 接口获取资源即可,location
代表具体文件路径。例如通过:
getResource("classpath:test.xml")
getResource("http://tycoding.cn/test.xml")
那么判断classpath:
和http://
分别使用那种 Resource 实现去获取资源则交由 ResourceLoader 接口的实现类实现,
策略模式
策略模式:策略模式是一种行为性模式,定义了一些列算法并封装起来,根据客户端的调用,通过不同的策略调用不同的实现。
在 Rresource 接口的实现上,策略模式可以认为是对工厂模式的进一步封装实现,通过不同的策略去查找使用哪种具体的工厂去实现。
在 Spring 对 Resource 的实现上,策略模式的实现可以看ResourcePatternResolver
接口,继承至 ResourceLoader 接口。ResourceLoader 工厂模式被用于查找和实现,而这个查找匹配过程交由策略类实现,通过一系列的策略决定选择使用哪个工厂选择符合条件的资源。
public interface ResourcePatternResolver extends ResourceLoader {
Resource[] getResources(String locationPattern) throws IOException;
}
例如:
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath*:**/*.xml");
ResourcePatternResolver 接口的默认实现是 PathMatchingResourcePatternResolver,支持 Ant-style 路径匹配的策略类。
因为要获取classpath
下的资源,所以 PathMatchingResourcePatternResolver 策略类匹配到所有 xml 结尾的文件后,最终还会通过ClassPathResource
类获取 Resource 资源,伪代码如下:
protected Resource getResource(String path) {
if (path.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
return new ClassPathResource(path.substring(CLASSPATH_ALL_URL_PREFIX.length()), getClassLoader());
}
...
}
DefaultResourceLoader
ResourceLoader 接口也有一个默认实现类:DefaultResourceLoader
。在手写 Spring 的项目:https://github.com/TyCoding/mini-spring 中我们可以用此类做一个简单的实现:
public class DefaultResourceLoader implements ResourceLoader {
public static final String CLASSPATH_URL_PREFIX = "classpath:";
@Override
public Resource getResource(String location) {
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
// 从本地相对路径获取
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()));
} else {
try {
// 作为URL处理
URL url = new URL(location);
return new UrlResource(url);
} catch (MalformedURLException e) {
// 从磁盘地址获取
return new FileSystemResource(location);
}
}
}
}
可以看到,其实就是根据某些字符串匹配条件,匹配的资源路径将会调用对应的 Resource 工厂类实现获取资源的逻辑。
Spring 源码专栏
此专栏将从 Spring 源码角度整体分析 Spring 设计思路以及常见的面试题。
配套作者的手写 Spring 的项目:https://github.com/TyCoding/mini-spring 。该项目中包含各个阶段的开发文档,有关 Spring 源码更详细的分析测试文档请查阅:https://github.com/TyCoding/mini-spring/tree/main/docs
联系我
-
个人博客:http://tycoding.cn/ -
GitHub:https://github.com/tycoding -
微信公众号:程序员涂陌
原文始发于微信公众号(程序员涂陌):Spring源码分析——资源文件加载
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/145605.html