1、SpringFramework 的资源模型是什么?都有哪几种类型?
关于资源加载,SpringFramework没有直接使用JDK的那一套:ClassLoader 的 getResource 和 getResourceAsStream 的方法,而是重新造了一套。
当然了,从源码中分析,其实最底层是使用了JDK的那一套 + 自定义。
SpringFramework 的资源模型,我们可以看从 InputStreamResource 的继承关系看。
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。