1、SpringFramework 的资源模型是什么?都有哪几种类型?

关于资源加载,SpringFramework没有直接使用JDK的那一套:ClassLoader 的 getResource 和 getResourceAsStream 的方法,而是重新造了一套。


当然了,从源码中分析,其实最底层是使用了JDK的那一套 + 自定义。


SpringFramework 的资源模型,我们可以看从 InputStreamResource 的继承关系看。

2021-07-02阅读小笔记:Spring ioc 之资源扫描_spring

SpringFramework的资源模型中,最上层的是 InputStreamSource,它非常简单,只有一个接口,就是返回输入流

public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}

而它有一个默认实现类:

  • EncodedResource,这个实现类支持对资源进行编码,里面组合了一个Resource resource,证明它本身不是加载资源的。

还有间接两个子接口:


  • ContextResource
  • WritableResource:支持对资源进行写操作。

2、SpringFramework 默认可以加载的资源都有哪些?顺序分别是什么?

默认加载的资源

Java 原生可以从下面三个地方加载文件:


  • 借助 ClassLoader 加载类路径下的资源
  • 借助 File 加载文件系统中的资源
  • 借助 URL 和不同的协议加载本地 / 网络上的资源

SpringFrameword 也是从这三个地方提供了不同的实现,详情可见 DefaultResourceLoader。

因为我们知道,ApplicationContext 在 BeanFactory 的基础上了扩展了很多特性,其中一个就是资源加载,提供的是「ResourceLoader」接口,而默认实现是 DefaultResourceLoader 类;我们也可以看到 ApplicationContext 的默认实现类 AbstractApplicationContext 也是继承了 DefaultResourceLoader。

那么我们可以从 DefaultResourceLoader 中是怎么对 Java 原生加载文件的方式提供了实现:

@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
// 遍历所有 ProtocolResolver,SpringFramewrok或我们可利用此做自定义资源扫描,只需要实现ProtocolResolver接口,再利用 DefaultResourceLoader 的 addProtocolResolver 方法传入到 DefaultResourceLoader 的 protocolResolvers 列表中。
for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
// 最后还是利用 ClassPathResource 获取类路径下的文件
if (location.startsWith("/")) {
return getResourceByPath(location);
}
// classpath: 前缀,利用 ClaspathResource 获取类路径下的文件
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);
// 根据判断利用 File 或 URL 获取
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}

/**
* ClassPathResource that explicitly expresses a context-relative path
* through implementing the ContextResource interface.
*/
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());
}
}

关于扫描顺序

从上面的 DefaultResourceLoader 的源码就可以看出来了:

protocolResolvers -> classpath -> file -> url。