转: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]);
}

主要分两步:

  1. 确定目录,获取该目录下得所有资源
  2. 在所获得的所有资源中进行迭代匹配获取想要的资源。

  一个是 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的更多相关文章

  1. 【sping揭秘】6、IOC容器之统一资源加载策略

    Spring中的resource 我们先看看类之间的关系 注意我们的application是间接继承了resourceloader的,也就是说我们的application其实就是一个resourcel ...

  2. 【死磕 Spring】----- IOC 之 Spring 统一资源加载策略

    原文出自:http://cmsblogs.com 在学 Java SE 的时候我们学习了一个标准类 java.net.URL,该类在 Java SE 中的定位为统一资源定位器(Uniform Reso ...

  3. 【死磕 Spring】—— IoC 之 Spring 统一资源加载策略

    本文主要基于 Spring 5.0.6.RELEASE 摘要: 原创出处 http://svip.iocoder.cn/Spring/IoC-load-Resource/ 在学 Java SE 的时候 ...

  4. ios资源加载策略

    做了好几个月的ios,大框架都是别人搭好的,自己只是实现逻辑,很是失落.慢慢开始整理学习一些概念类的东西吧,希望自己能提高点. cocos2d-x从cocos2d-2.0-x-2.0.2开始,考虑到自 ...

  5. 简说Spring中的资源加载

    声明: 本文若有 任何纰漏.错误,请不吝指正!谢谢! 问题描述 遇到一个关于资源加载的问题,因此简单的记录一下,对Spring资源加载也做一个记录. 问题起因是使用了@PropertySource来进 ...

  6. Spring中的资源加载

    大家也都知道JDK的类加载器:BootStrap ClassLoader.ExtenSion ClassLoader.Application ClassLoader:也使用了双亲委派模型,主要是为了防 ...

  7. 【死磕 Spring】----- IOC 之 加载 Bean

    原文出自:http://cmsblogs.com 先看一段熟悉的代码: ClassPathResource resource = new ClassPathResource("bean.xm ...

  8. spring资源访问接口和资源加载接口

    spring 资源访问接口 JDK提供的资源访问类,如java.net.URL.File等,不能很好地满足各种资源的访问需求,比如缺少从类路径或者Web容器的上下文中获取资源的操作类. 鉴于此,spr ...

  9. 手撸Spring框架,设计与实现资源加载器,从Spring.xml解析和注册Bean对象

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你写的代码,能接的住产品加需求吗? 接,是能接的,接几次也行,哪怕就一个类一片的 i ...

  10. Spring资源加载器抽象和缺省实现 -- ResourceLoader + DefaultResourceLoader(摘)

    概述 对于每一个底层资源,比如文件系统中的一个文件,classpath上的一个文件,或者一个以URL形式表示的网络资源,Spring 统一使用 Resource 接口进行了建模抽象,相应地,对于这些资 ...

随机推荐

  1. vulnhub靶场之Beelzebub

    准备: 攻击机:虚拟机kali.本机win10. 靶机:Beelzebub: 1,网段地址我这里设置的桥接,所以与本机电脑在同一网段,下载地址:https://download.vulnhub.com ...

  2. 开发用户K8S授权

    #开发用户没有K8S权限 [ans@master ~]$ kubectl get po Unable to connect to the server: x509: certificate signe ...

  3. JMX port被占用

    JMX port被占用 解决方案 win+R打开DOS窗口,进入window命令,注意:要以管理员身份打开(快捷键:ctrl+shift+enter): 使用命令:netstat -aon|finds ...

  4. Day22:多态详解

    方法的多态 1.1什么是多态? 指一个对象在不同时刻拥有不同的形态. 例:猫 cat=new 猫(): ​ 动物 animal=new 猫(): 多态建立的条件: 建立在继承的关系上: 有方法重写: ...

  5. Forest + IDEA = 双倍快乐!ForestX 隆重登场

    Forest + IDEA = 双倍快乐!ForestX 隆重登场 Forest 是一款声明式的 Java 开源 HTTP 框架,相比它的前辈 Httpclient 和 OkHttp 更简明易懂.也更 ...

  6. 长度最小子数组-LeetCode209 滑动窗口

    力扣:https://leetcode.cn/problems/minimum-size-subarray-sum/ 题目 给定一个含有 n 个正整数的数组和一个正整数 target .找出该数组中满 ...

  7. day31-JQuery04

    JQuery04 6.jQuery的DOM操作02 6.9常用遍历节点方法 取得匹配元素的所有子元素组成的集合:children(),该方法只考虑子元素而不考虑任何后代元素 取得匹配元素后面的同辈元素 ...

  8. 【Java SE】Day08 String类、static关键字、Arrays类、Math类

    一.String类 1.概述 所有双引号字符串,都是String类的对象 字符串常量,会存在字符串常量池中 2.创建 构造函数--空构造.字符数组.字节(byte ASCII码)数组 3.常用方法-- ...

  9. 第k个数【模板题】

    第k个数 给定一个长度为 \(n\) 的整数数列,以及一个整数 \(k\),请用快速选择算法求出数列从小到大排序后的第 \(k\) 个数. 输入格式 第一行包含两个整数 \(n\) 和 \(k\). ...

  10. 题解P3847 [TJOI2007]调整队形

    简要题意 给出一个长度为 \(n\) 的序列 \(A\),你需要执行下面的操作,将这个序列变成回文序列: 在序列左右侧或中间插入一个元素,元素数值任意. 删除一个元素. 更改一个元素的值. \(1 \ ...