Spring中的资源模型
ClassLoader类的getResource和getResourceAsStream方法是原生JDK中内置的资源加载文件的方式;
Spring中资源模型顶级接口不是Resource,而是InputStreamSource接口;
Spring为何自己实现一套资源加载方式?
主要原因是JDK原生的URL资源加载方式,对于加载classpath或ServletContext中的资源没有标准的处理;
参考:[https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#resources-introduction]
InputStreamSource
InputStreamSource接口只有一个getInputStream方法,实现了该接口的实现类都可以从中获取资源的输入流;
Resource
Resource是InputStreamSource的子接口,它是从底层资源的实际类型(例如文件或类路径资源)中抽象出来的资源描述符的接口,它代表的是可读的资源;
EncodedResource
从类名可以看出它是编码后的资源,它的内部有个Resource类型的属性,说明它本身不是直接加载资源的;
WritableResource
从Spring3.1之后,Resource有了一个新的子接口:WritableResource,它代表可写的资源;
ContextResource
ContextResource是从一个封闭的上下文中加载的资源(类似于ServletContext这种)的扩展接口,例如来自javax.servlet.ServletContext,也可以来自普通的类路径路径或相对的文件系统路径(在没有显式前缀的情况下指定,因此相对于本地ResourceLoader的上下文应用);
Spring中资源加载方式
-
ClassLoader
ClassPathResource [classpath:/]
如果是classpath开头的资源路径,Spring解析后会自动到类路径下找;
-
File
FileSystemResource [file:/]
如果是file开头的资源路径,则会去文件系统中找;
-
URL
UrlResource [xxx:/]
如果是 URL 支持的协议开头,则底层会使用对应的协议,去尝试获取相应的资源文件;
除了这三种实现,还有对应于ContextResource的实现ServletContextResource,它是表示资源去ServletContext域中找;
Spring加载资源分析
ResourcePatternResolver它的父接口ResourceLoader是真正负责加载资源的,而AbstraactApplicationContext继承DefaultResourceLoader,也就是说ApplicationContext具有ResourceLoader加载资源的能力;
ResourcePatternResolver
ResourcePatternResolver从类名理解可以解释为资源模式解析器,实际上它是根据特定的路径去解析资源文件的;
ResourcePatternResolver本身还是ResourceLoader的扩展,ResourceLoader实现最基本的解析,ResourcePatternResolver可以支持Ant形式的带星号( * )的路径解析;
PathMathcingResourcePatternResolver是一个独立的实现,可在ApplicationContext外部使用,ResourceArrayPropertyEditor使用它来填充Resource数组中Bean属性,基于路径匹配的解析器这种实现会根据特殊的路径来返回多个匹配到的资源文件;
ResourcePatternResolver可以与任何类型的位置模式一起使用(如:”/WEB-INF/*-context.xml”),输入模式必须与策略实现相匹配,该接口指定转换方法,而不是特定的模式格式;
它支持的Ant风格的匹配模式,写法如下:
- /WEB-INF/*.xml:匹配/WEB-INF目录下的任意xml文件
- /WEB-INF/**/beans-*.xml:匹配/WEB-INF下面任意层级目录的beans-开头的xml文件
- /**/*.xml:匹配任意的xml文件
ResourcePatternResolver可以匹配类路径下的资源文件,方式是在资源路径中加一个classpath*:的前缀;ResourcePatternResolver不仅可以匹配Web工程中的webapps文件,还可以匹配classpath下的文件;
DefaultResourceLoader根据路径加载到对应的资源
DefaultResourceLoader#getResource
DefaultResourceLoader会根据内部的ProtocolResolver的集合来尝试加载资源;
在上图中第二框中第一个if中getResourceByPath逻辑如下;
getResourceByPath方法是可以被重写的,如引入了spring-web模块中的依赖,这时getResourceByPath会被几个Web级的子类重写该方法,如GenericApplicationContext;
GenericApplicationContext#getResourceByPath
第二个else if是解析类路径下的资源;
第三个else是如果DefaultResourceLoader不能处理类路径下的文件,就会尝试通过URL方式加载;
ProtocolResolver
ProtocolResolver是Spring4.3版本出现的,它本身可以搭配ResourceLoader,在ApplicationContext中实现自定义协议的资源加载,但它还可以脱离ApplicationContext,直接跟ResourceLoader搭配即可;
ProtocolResolver使用示例
在resources目录下创建一个resource目录,并在resource目录下创建一个demo.txt文件,demo.txt文件内容可以随意,当程序运行时会从该路径下加载,并通过缓冲流读取;
查看代码
public class DemoProtocolResolver implements ProtocolResolver {
public static final String DEMO_PATH_PREFIX = "demo:";
@Override
public Resource resolve(String location, ResourceLoader resourceLoader) {
if (!location.startsWith(DEMO_PATH_PREFIX)) {
return null;
}
String realpath = location.substring(DEMO_PATH_PREFIX.length());
String classpathLocation = "classpath:resource/" + realpath;
return resourceLoader.getResource(classpathLocation);
}
public static void main(String[] args) throws Exception {
// 实例化DefaultResourceLoader DemoProtocolResolver
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
DemoProtocolResolver demoProtocolResolver = new DemoProtocolResolver();
resourceLoader.addProtocolResolver(demoProtocolResolver);
// ResourceLoader获取编写的demo.txt
Resource resource = resourceLoader.getResource("demo:demo.txt");
InputStream inputStream = resource.getInputStream();
InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(reader);
String readLine;
while ((readLine = br.readLine()) != null) {
System.out.println(readLine);
}
br.close();
}
}
原创文章,作者:dweifng,如若转载,请注明出处:https://blog.ytso.com/273497.html