Spring 01 统一资源加载策略 Resource和ResourceLoader
转:https://www.cnblogs.com/loveLands/articles/10797772.html
1 Resource统一资源
1.1 简介
处理外部资源是很繁琐的事情,我们可能需要处理URL资源、File资源资源、ClassPath相关资源、服务器相关资源(JBoss AS 5.x上的VFS资源)等等很多资源;
而且处理这些资源步骤都是类似的(打开资源、读取资源、关闭资源),因此如果能抽象出一个统一的接口来对这些底层资源进行统一访问,是不是很方便,而且使我们系统更加简洁。
Spring 提供Resource接口来统一这些底层资源一致的访问,作为所有资源的统一抽象。
1.2 Resource源码
public interface Resource extends InputStreamSource {
//返回当前Resource代表的资源是否存在,true表示存在
boolean exists(); //返回当前Resource代表的资源是否可读,true表示可读
default boolean isReadable() {
return exists();
} //返回当前Resource代表的资源是否已经打开,如果返回true,则只能被读取一次然后关闭以避免资源泄露;常见的Resource实现一般返回false
default boolean isOpen() {
return false;
} //是否是文件资源
default boolean isFile() {
return false;
}
//如果当前Resource代表的资源能由java.util.URL代表,则返回该URL,否则抛出IOException
URL getURL() throws IOException; //如果当前Resource代表的资源能由java.util.URI代表,则返回该URI,否则抛出IOException
URI getURI() throws IOException; //如果当前Resource代表的底层资源能由java.io.File代表,则返回该File,否则抛出IOException
File getFile() throws IOException; //返回 ReadableByteChannel
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
} //返回当前Resource代表的底层文件资源的长度,一般是值代表的文件资源的长度
long contentLength() throws IOException; //返回当前Resource代表的底层资源的最后修改时间
long lastModified() throws IOException; //用于创建相对于当前Resource代表的底层资源的资源,比如当前Resource代表文件资源“d:/test/”则createRelative(“test.txt”)将返回表文件资源“d:/test/test.txt”Resource资源
Resource createRelative(String relativePath) throws IOException; //返回当前Resource代表的底层文件资源的文件路径,比如File资源“file://d:/test.txt”将返回“d:/test.txt”,而URL资源http://www.javass.cn将返回“”,因为只返回文件路径
@Nullable
String getFilename(); //返回当前Resource代表的底层资源的描述符,通常就是资源的全路径(实际文件名或实际URL地址)
String getDescription(); }
从Resource源码中可以看到,Resource提供了判断资源是否存在,是否可访问,是什么类型资源,获取资源长度、上次修改时间、资源路径等等的方法。为我们提供了统一的资源访问
1.3 Resource的体系
从上图可以看到,Resource 根据资源的不同类型提供不同的具体实现
1)FileSystemResource:对 java.io.File 类型资源的封装,支持文件和 URL 的形式,实现 WritableResource 接口,从 Spring Framework 5.0 开始,FileSystemResource 使用NIO.2 API进行读/写交互
2)ByteArrayResource:对字节数组提供的数据的封装。如果通过 InputStream 形式访问该类型的资源,该实现会根据字节数组的数据构造一个相应的 ByteArrayInputStream。
3)UrlResource:对 java.net.URL类型资源的封装。内部委派 URL 进行具体的资源操作。
4)ClassPathResource:class path 类型资源的实现。使用给定的 ClassLoader 或者给定的 Class 来加载资源。
5)InputStreamResource:将给定的 InputStream 作为一种资源的 Resource 的实现类。
我们发现他们都继承及抽象类AbstractResource。AbstractResource 为 Resource 接口的默认实现,已经替我们实现Resource 接口的大部分的公共实现。如果我们想要实现自定义的 Resource,记住不要实现 Resource 接口,而应该继承 AbstractResource 抽象类,然后根据当前的具体资源特性覆盖相应的方法即可
1.4 AbstractResource
1.4.1 AbstractResource源码
public abstract class AbstractResource implements Resource { /**
* 判断文件是否存在,若判断过程产生异常(因为会调用SecurityManager来判断),就关闭对应的流
*/
@Override
public boolean exists() {
try {
return getFile().exists();
}
catch (IOException ex) {
// Fall back to stream existence: can we open the stream?
try {
InputStream is = getInputStream();
is.close();
return true;
}
catch (Throwable isEx) {
return false;
}
}
} /**
* 直接返回true,表示可读
*/
@Override
public boolean isReadable() {
return true;
} /**
* 直接返回 false,表示未被打开
*/
@Override
public boolean isOpen() {
return false;
} /**
* 直接返回false,表示不为 File
*/
@Override
public boolean isFile() {
return false;
} /**
* 抛出 FileNotFoundException 异常,交给子类实现
*/
@Override
public URL getURL() throws IOException {
throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");
} /**
* 基于 getURL() 返回的 URL 构建 URI
*/
@Override
public URI getURI() throws IOException {
URL url = getURL();
try {
return ResourceUtils.toURI(url);
}
catch (URISyntaxException ex) {
throw new NestedIOException("Invalid URI [" + url + "]", ex);
}
} /**
* 抛出 FileNotFoundException 异常,交给子类实现
*/
@Override
public File getFile() throws IOException {
throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
} /**
* 根据 getInputStream() 的返回结果构建 ReadableByteChannel
*/
@Override
public ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
} /**
* 获取资源的长度
*
* 这个资源内容长度实际就是资源的字节长度,通过全部读取一遍来判断
*/
@Override
public long contentLength() throws IOException {
InputStream is = getInputStream();
try {
long size = 0;
byte[] buf = new byte[255];
int read;
while ((read = is.read(buf)) != -1) {
size += read;
}
return size;
}
finally {
try {
is.close();
}
catch (IOException ex) {
}
}
} /**
* 返回资源最后的修改时间
*/
@Override
public long lastModified() throws IOException {
long lastModified = getFileForLastModifiedCheck().lastModified();
if (lastModified == 0L) {
throw new FileNotFoundException(getDescription() +
" cannot be resolved in the file system for resolving its last-modified timestamp");
}
return lastModified;
} protected File getFileForLastModifiedCheck() throws IOException {
return getFile();
} /**
* 交给子类实现
*/
@Override
public Resource createRelative(String relativePath) throws IOException {
throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());
} /**
* 获取资源名称,默认返回 null
*/
@Override
@Nullable
public String getFilename() {
return null;
} /**
* 返回资源的描述
*/
@Override
public String toString() {
return getDescription();
} @Override
public boolean equals(Object obj) {
return (obj == this ||
(obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));
} @Override
public int hashCode() {
return getDescription().hashCode();
}
}
从源码中我们看到,它已经实现了Resource定义的功能。
2 ResourceLoader统一资源定位
Spring 将资源的定义和资源的加载区分开,Resource 定义统一的资源,那资源的加载则由 ResourceLoader 来统一定义。
2.1 ResourceLoader源码
package org.springframework.core.io; import org.springframework.lang.Nullable;
import org.springframework.util.ResourceUtils; public interface ResourceLoader {
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
Resource getResource(String location);
@Nullable
ClassLoader getClassLoader();
}
2.2 getResource
()方法
从源码中看到,ResourceLoader 接口提供两个方法:getResource()
、getClassLoader()
getResource()
根据所提供资源的路径 location 返回 Resource 实例,但是它不确保该 Resource 一定存在。
该方法支持以下模式的资源加载:
URL位置资源,如”file:C:/test.dat”
ClassPath位置资源,如”classpath:test.dat”
相对路径资源,如”WEB-INF/test.dat”,此时返回的Resource实例根据实现不同而不同
该方法的主要实现是在其子类 DefaultResourceLoader 中实现
2.3 getClassLoader
()方法
getClassLoader()
返回 ClassLoader 实例,对于想要获取 ResourceLoader 使用的 ClassLoader 用户来说,可以直接调用该方法来获取
2.4 ResourceLoader的体系
Spring 统一的资源加载器,它提供了统一的抽象,具体的实现则由相应的子类来负责实现,其类的类结构图如下
2.5 DefaultResourceLoader
2.5.1 DefaultResourceLoader源码
public class DefaultResourceLoader implements ResourceLoader { @Nullable
private ClassLoader classLoader; private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4); private final Map<Class<?>, Map<Resource, ?>> resourceCaches = new ConcurrentHashMap<>(4); public DefaultResourceLoader() {
this.classLoader = ClassUtils.getDefaultClassLoader();
} public DefaultResourceLoader(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
} public void setClassLoader(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
} @Override
@Nullable
public 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 Collection<ProtocolResolver> getProtocolResolvers() {
return this.protocolResolvers;
} @SuppressWarnings("unchecked")
public <T> Map<Resource, T> getResourceCache(Class<T> valueType) {
return (Map<Resource, T>) this.resourceCaches.computeIfAbsent(valueType, key -> new ConcurrentHashMap<>());
} public void clearResourceCaches() {
this.resourceCaches.clear();
} @Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null"); for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
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);
}
}
} protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
} protected static class ClassPathContextResource extends ClassPathResource implements ContextResource { public ClassPathContextResource(String path, @Nullable ClassLoader classLoader) {
super(path, classLoader);
} @Override
public String getPathWithinContext() {
return getPath();
} @Override
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);
return new ClassPathContextResource(pathToUse, getClassLoader());
}
} }
2.5.2 DefaultResourceLoader简介
DefaultResourceLoader 是 ResourceLoader 的默认实现,接收 ClassLoader 作为构造函数的参数或者使用不带参数的构造函数,在使用不带参数的构造函数时,使用的 ClassLoader 为默认的 ClassLoader(一般为Thread.currentThread().getContextClassLoader()
),可以通过 ClassUtils.getDefaultClassLoader()
获取。当然也可以调用 setClassLoader()
方法设置使用的ClassLoader。
下面是它的构造方法
public DefaultResourceLoader() {
this.classLoader = ClassUtils.getDefaultClassLoader();
} public DefaultResourceLoader(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
} public void setClassLoader(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
} @Override
@Nullable
public ClassLoader getClassLoader() {
return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());
}
2.5.3 它的核心方法getResource
()
ResourceLoader中最核心的方法为 getResource()
,根据提供的 location 返回相应的 Resource,而 DefaultResourceLoader 对该方法提供核心实现,如下
public 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);
}
}
}
1)首先通过 ProtocolResolver 来加载资源,成功返回 Resource,否则调用如下逻辑
2)若 location 以 / 开头,则调用 getResourceByPath()构造 ClassPathContextResource 类型资源并返回。
3)若 location 以 classpath: 开头,则构造 ClassPathResource 类型资源并返回,在构造该资源时,通过 getClassLoader()获取当前的 ClassLoader。
4)构造 URL ,尝试通过它进行资源定位,若没有抛出 MalformedURLException 异常,有则判断是否为 FileURL , 如果是则构造 FileUrlResource 类型资源,否则构造 UrlResource。
5)若在加载过程中抛出 MalformedURLException 异常,则委派 getResourceByPath() 实现资源定位加载
2.5.4 ProtocolResolver介绍
在getResource方法中,开始有这么一段代码,它会先去采用protocolResolver去加载资源
for (ProtocolResolver protocolResolver : this.protocolResolvers) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
this.protocolResolvers,它是一个set集合(ProtocolResolver)
private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);
我们来看看ProtocolResolver 是什么
2.5.4.1 ProtocolResolver 源码
@FunctionalInterface
public interface ProtocolResolver { @Nullable
Resource resolve(String location, ResourceLoader resourceLoader); }
2.5.4.2 ProtocolResolver 介绍
ProtocolResolver ,用户自定义协议资源解决策略,作为 DefaultResourceLoader 的 SPI,允许用户自定义资源加载协议,而不需要继承 ResourceLoader 的子类。在介绍 Resource 时,提到如果要实现自定义 Resource,我们只需要继承 DefaultResource 即可。而自定义ResourceLoader,有了 ProtocolResolver 后,不需要直接继承 DefaultResourceLoader,改为实现 ProtocolResolver 接口也可以实现自定义的 ResourceLoader,
ProtocolResolver 接口,仅有一个方法 Resource resolve(String location, ResourceLoader resourceLoader)
,该方法接收两个参数:资源路径location和指定的加载器 ResourceLoader,返回为相应的 Resource 。在 Spring 中该接口并没有实现类,它需要用户自定义,自定义的 Resolver 如何加入 Spring 体系呢?调用 DefaultResourceLoader.addProtocolResolver()
即可,如下
public void addProtocolResolver(ProtocolResolver resolver) {
Assert.notNull(resolver, "ProtocolResolver must not be null");
this.protocolResolvers.add(resolver);
}
也就是说我们可以通过实现ProtocolResolve来自定义我们自己的ResourceLoader
2.5.4.3 示例
1)通过FileSystemResourceLoader去加载resource,成功加载到文件资源
FileSystemResourceLoader fl = new FileSystemResourceLoader();
Resource resource = fl.getResource("C:\\Users\\Administrator\\Desktop\\18年居养模式(1).XLSX");
System.out.println(resource.exists());
System.out.println(resource.isFile());
System.out.println(resource.isReadable());
System.out.println(resource.isOpen());
System.out.println(resource.getURL());
System.out.println(resource.getURI());
2)通过 DefaultResourceLoader 去加载resource,加载到的是一个ClassPathContextResource,加载文件资源失败
DefaultResourceLoader f2 = new DefaultResourceLoader();
Resource resource2 = f2.getResource("C:\\Users\\Administrator\\Desktop\\18年居养模式(1).XLSX");
System.out.println(resource2.exists());
System.out.println(resource2.isFile());
System.out.println(resource2.isReadable());
System.out.println(resource2.isOpen());
System.out.println(resource2.getURL());
System.out.println(resource2.getURI());
3)和上面例子一样通过 DefaultResourceLoader 去加载resource,但是在DefaultResourceLoader加入了一个自定义的ProtocolResolver,加载成功
DefaultResourceLoader f3 = new DefaultResourceLoader();
f2.addProtocolResolver(new myProtocolResolver());
Resource resource3 = f3.getResource("C:\\Users\\Administrator\\Desktop\\18年居养模式(1).XLSX");
System.out.println(resource3.exists());
System.out.println(resource3.isFile());
System.out.println(resource3.isReadable());
System.out.println(resource3.isOpen());
System.out.println(resource3.getURL());
System.out.println(resource3.getURI());
public static class myProtocolResolver implements ProtocolResolver{ @Override
public Resource resolve(String location, ResourceLoader resourceLoader) {
System.out.println("自定义加载资源");
FileSystemResourceLoader fl = new FileSystemResourceLoader();
Resource resource = fl.getResource("C:\\Users\\Administrator\\Desktop\\18年居养模式(1).XLSX");
return resource;
}
}
2.6 FileSystemResourceLoader
FileSystemResourceLoader ,它继承 DefaultResourceLoader 且覆写 getResourceByPath(String)
,使之从文件系统加载资源并以 FileSystemResource 类型返回,就可以得到想要的资源类型
2.6.1 FileSystemResourceLoader源码
public class FileSystemResourceLoader extends DefaultResourceLoader { @Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemContextResource(path);
} private static class FileSystemContextResource extends FileSystemResource implements ContextResource { public FileSystemContextResource(String path) {
super(path);
} @Override
public String getPathWithinContext() {
return getPath();
}
} }
2.7 ResourcePatternResolver
2.7.1 源码
public interface ResourcePatternResolver extends ResourceLoader { String CLASSPATH_ALL_URL_PREFIX = "classpath*:"; Resource[] getResources(String locationPattern) throws IOException; }
2.7.2 简介
ResourceLoader 的 getResource(String location)方法
每次只能根据 location 返回一个 Resource,当需要加载多个资源时,除了多次调用 getResource()
外别无他法。
而ResourcePatternResolver 是 ResourceLoader 的扩展,它支持根据指定的资源路径匹配模式每次返回多个 Resource 实例
ResourcePatternResolver 在 ResourceLoader 的基础上增加Resource[] getResources(String locationPattern)方法
,以支持根据路径匹配模式返回多个 Resource 实例,同时也新增一种新的协议前缀 classpath*:
,该协议前缀由其子类负责实现。
2.8 PathMatchingResourcePatternResolver
2.8.1 简介
PathMatchingResourcePatternResolver 为 ResourcePatternResolver 最常用的子类,除了支持 ResourceLoader 和 ResourcePatternResolver 新增的 classpath*: 前缀外,还支持 Ant 风格的路径匹配模式(类似于 **/*.xml
)
2.8.2 构造方法
PathMatchingResourcePatternResolver 提供三个构造方法,如下:
public PathMatchingResourcePatternResolver() {
this.resourceLoader = new DefaultResourceLoader();
} public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
this.resourceLoader = resourceLoader;
} public PathMatchingResourcePatternResolver(@Nullable ClassLoader classLoader) {
this.resourceLoader = new DefaultResourceLoader(classLoader);
}
PathMatchingResourcePatternResolver 在实例化的时候,可以指定一个 ResourceLoader,如果不指定的话,它会在内部构造一个 DefaultResourceLoader
2.8.3 getResource(String location)方法
@Override
public Resource getResource(String location) {
return getResourceLoader().getResource(location);
}
getResource()
方法中并没有自己来完成加载,而是直接委托给相应的其它的ResourceLoader 来实现,如果在实例化PathMatchingResourcePatternResolver 的时候,不知道 ResourceLoader ,那么在加载资源时,默认就是 DefaultResourceLoader来完成加载的
2.8.4 getResources方法
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null"); // 以 classpath*: 开头
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
// 路径包含通配符
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
return findPathMatchingResources(locationPattern);
}
else {
// 路径不包含通配符
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
locationPattern.indexOf(':') + 1);
// 路径包含通配符
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
return findPathMatchingResources(locationPattern);
}
else {
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
其加载逻辑如下
2.8.5 findAllClassPathResources()方法
当 locationPattern 以 classpath*: 开头但是不包含通配符,则调用findAllClassPathResources() 方法加载资源。该方法返回 classes 路径下和所有 jar 包中的所有相匹配的资源
protected Resource[] findAllClassPathResources(String location) throws IOException {
String path = location;
if (path.startsWith("/")) {
path = path.substring(1);
}
Set<Resource> result = doFindAllClassPathResources(path);
if (logger.isDebugEnabled()) {
logger.debug("Resolved classpath location [" + location + "] to resources " + result);
}
return result.toArray(new Resource[0]);
}
doFindAllClassPathResources()
方法
protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
Set<Resource> result = new LinkedHashSet<>(16);
ClassLoader cl = getClassLoader();
Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
while (resourceUrls.hasMoreElements()) {
URL url = resourceUrls.nextElement();
result.add(convertClassLoaderURL(url));
}
if ("".equals(path)) {
addAllClassLoaderJarRoots(cl, result);
}
return result;
}
doFindAllClassPathResources()
根据 ClassLoader 加载路径下的所有资源。在加载资源过程中,如果在构造 PathMatchingResourcePatternResolver 实例的时候,如果传入ClassLoader,则调用其 getResources()
,否则调用ClassLoader.getSystemResources(path)
。 ClassLoader.getResources()
如下:
public Enumeration<URL> getResources(String name) throws IOException {
@SuppressWarnings("unchecked")
Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
if (parent != null) {
tmp[0] = parent.getResources(name);
} else {
tmp[0] = getBootstrapResources(name);
}
tmp[1] = findResources(name); return new CompoundEnumeration<>(tmp);
}
findAllClassPathResources()
其实就是利用 ClassLoader 来加载指定路径下的资源。如果我们传入的路径为空或者 /
,则会调用 addAllClassLoaderJarRoots()
方法加载所有的 jar 包
2.8.6 findPathMatchingResources方法
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
// 确定跟路径
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length()); // 获取根据路径下得资源
Resource[] rootDirResources = getResources(rootDirPath); Set<Resource> result = new LinkedHashSet<>(16);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
URL rootDirUrl = rootDirResource.getURL();
// bundle 资源类型
if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
if (resolvedUrl != null) {
rootDirUrl = resolvedUrl;
}
rootDirResource = new UrlResource(rootDirUrl);
} // VFS 资源
if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
} // Jar
else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
}
else {
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}
}
if (logger.isDebugEnabled()) {
logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return result.toArray(new Resource[0]);
}
主要分两步:
- 确定目录,获取该目录下得所有资源
- 在所获得的所有资源中进行迭代匹配获取想要的资源。
一个是 determineRootDir()
,一个是 doFindPathMatchingFileResources()
determineRootDir()
主要是用于确定根路径,如下:
protected String determineRootDir(String location) {
int prefixEnd = location.indexOf(':') + 1;
int rootDirEnd = location.length();
while (rootDirEnd > prefixEnd && getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd))) {
rootDirEnd = location.lastIndexOf('/', rootDirEnd - 2) + 1;
}
if (rootDirEnd == 0) {
rootDirEnd = prefixEnd;
}
return location.substring(0, rootDirEnd);
}
该方法一定要给出一个确定的根目录。该根目录用于确定文件的匹配的起始点,将根目录位置的资源解析为 java.io.File
并将其传递到 retrieveMatchingFiles()
确定根路径后,则调用 getResources()
方法获取该路径下得所有资源,然后迭代资源获取符合条件的资源。
3 Spring 整个资源记载过程简要总结下
Spring 提供Resource 和 ResourceLoader 来统一抽象整个资源及其定位。使得资源与资源的定位有一个更加清晰的界限,并且提供合适的 Default 类,使得自定义实现更加方便和清晰。
DefaultResource 为 Resource 的默认实现,它对 Resource 接口做一个统一的实现,子类继承该类后只需要覆盖相应的方法即可,同时对于自定义的 Resource 也是继承该类
DefaultResourceLoader 同样也是 ResourceLoader 的默认实现,在自定 义ResourceLoader 的时候除了可以继承该类外还可以实现 ProtocolResolver 接口来实现自定资源加载协议。
DefaultResourceLoader 每次只能返回单一的资源,所以 Spring 针对这个提供另外一个接口 ResourcePatternResolver ,该接口提供根据指定的 locationPattern 返回多个资源的策略。其子类 PathMatchingResourcePatternResolver 是一个集大成者的 ResourceLoader ,因为它即实现 Resource getResource(String location)
也实现 Resource[] getResources(String locationPattern)
Spring 01 统一资源加载策略 Resource和ResourceLoader的更多相关文章
- 【sping揭秘】6、IOC容器之统一资源加载策略
Spring中的resource 我们先看看类之间的关系 注意我们的application是间接继承了resourceloader的,也就是说我们的application其实就是一个resourcel ...
- 【死磕 Spring】----- IOC 之 Spring 统一资源加载策略
原文出自:http://cmsblogs.com 在学 Java SE 的时候我们学习了一个标准类 java.net.URL,该类在 Java SE 中的定位为统一资源定位器(Uniform Reso ...
- 【死磕 Spring】—— IoC 之 Spring 统一资源加载策略
本文主要基于 Spring 5.0.6.RELEASE 摘要: 原创出处 http://svip.iocoder.cn/Spring/IoC-load-Resource/ 在学 Java SE 的时候 ...
- ios资源加载策略
做了好几个月的ios,大框架都是别人搭好的,自己只是实现逻辑,很是失落.慢慢开始整理学习一些概念类的东西吧,希望自己能提高点. cocos2d-x从cocos2d-2.0-x-2.0.2开始,考虑到自 ...
- 简说Spring中的资源加载
声明: 本文若有 任何纰漏.错误,请不吝指正!谢谢! 问题描述 遇到一个关于资源加载的问题,因此简单的记录一下,对Spring资源加载也做一个记录. 问题起因是使用了@PropertySource来进 ...
- Spring中的资源加载
大家也都知道JDK的类加载器:BootStrap ClassLoader.ExtenSion ClassLoader.Application ClassLoader:也使用了双亲委派模型,主要是为了防 ...
- 【死磕 Spring】----- IOC 之 加载 Bean
原文出自:http://cmsblogs.com 先看一段熟悉的代码: ClassPathResource resource = new ClassPathResource("bean.xm ...
- spring资源访问接口和资源加载接口
spring 资源访问接口 JDK提供的资源访问类,如java.net.URL.File等,不能很好地满足各种资源的访问需求,比如缺少从类路径或者Web容器的上下文中获取资源的操作类. 鉴于此,spr ...
- 手撸Spring框架,设计与实现资源加载器,从Spring.xml解析和注册Bean对象
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你写的代码,能接的住产品加需求吗? 接,是能接的,接几次也行,哪怕就一个类一片的 i ...
- Spring资源加载器抽象和缺省实现 -- ResourceLoader + DefaultResourceLoader(摘)
概述 对于每一个底层资源,比如文件系统中的一个文件,classpath上的一个文件,或者一个以URL形式表示的网络资源,Spring 统一使用 Resource 接口进行了建模抽象,相应地,对于这些资 ...
随机推荐
- 用 vue3 中的 reduce(累加器) 随机生成100个字母,放入数组中,统计每个字母出现的次数
一.首先不用 reduce() 来实现 代码如下: <template lang=""> <div> <h1>统计每个字母出现的次数,不使用r ...
- Ueditor、FCKeditor、Kindeditor编辑器漏洞
Ueditor.FCKeditor.Kindeditor编辑器漏洞 免责声明: Ueditor编辑器漏洞 文件上传漏洞 XSS漏洞 SSRF漏洞 FCKeditor编辑器漏洞 查看FCKeditor版 ...
- 利用KubeEdge在A500部署边缘推理任务
利用KubeEdge在A500部署边缘推理任务 目 录 1 环境介绍... 1 2 云端环境部署... 2 2.1 在master节点安装Docker和k8S (ubuntu) 2 2.1.1 ...
- sqlserver数据库还原
这里是从A服务器备份的数据库文件,还原到本地B电脑的数据库.因数据库版本相同,故而未曾出现其他版本问题 1.有个bak的备份文件 2.在B电脑新建一个数据库test. 3.设置test数据库的访问限制 ...
- UEFI引导linux启动过程的顺序,及修改办法
注意这里我说的是 uefi 启动 启动过程顺序 正常安装完一个Linux系统,硬盘会被分成俩个分区,一块是 fat32 文件系统(启动项存放位置),一块是ext4 文件系统(也就是系统真正的安装位置) ...
- Vue2基本组件间通信
Vue2组件通信的基础方式 自己的理解:组件化通信,无非就是数据你传我,我传你,两个组件的相互交流,方法很多,下方有图示(此篇建议小白阅读,大神的话也不会看,哈哈哈哈!仅供参考,有不同的意见可以一起交 ...
- 1.1 大数据简介-hadoop-最全最完整的保姆级的java大数据学习资料
目录 1 hadoop-最全最完整的保姆级的java大数据学习资料 1.1 大数据简介 1.1.1 大数据的定义 1.1.2 大数据的特点 1.1.3 大数据的应用场景 1.1.4 大数据的发展趋势及 ...
- Go | 闭包的使用
闭包基本介绍 闭包就是 一个函数 和其相关的 引用环境 组合的一个整体 好处: 保存引用的变量,下次继续使用,不会销毁 下面通过闭包的方式,写一个数字累加器,体验一下闭包的妙处 闭包实现数字累加 pa ...
- <三>function函数对象类型的应用示例
std::function是一组函数对象包装类的模板,实现了一个泛型的回调机制.function与函数指针比较相似,优点在于它允许用户在目标的实现上拥有更大的弹性,即目标既可以是普通函数,也可以是函数 ...
- 「Docker学习系列教程」9-Docker容器数据卷介绍
通过前面8篇文章的学习,我们已经学会了docker的安装.docker常用的命令已经docker镜像修改后提交的远程镜像仓库及提交到公司的私服仓库中.接下来,我们再来学学Docker另外一个重要的东西 ...