ResourceLoader作为 Spring 统一的资源加载器 ,我们来看一下它的ULM图
ResourceLoader 接口提供两个方法:`getResource()、getClassLoader()。
getResource(String location)
方法,可以根据一个资源地址加载资源文件,资源地址的表达式可以是以下几种:
该方法的主要实现是在其子类 DefaultResourceLoader 中实现,具体过程我们在分析 DefaultResourceLoader 时做详细说明。 (注意,该方法不支持解析ant风格的资源路径表达式。)
Ant风格,为请求路径的一种匹配方式。
ANT通配符有三种:
通配符 | 说明 |
---|---|
? | 匹配任何单字符 |
* | 匹配0或者任意数量的字符 |
** | 匹配0或者更多的目录 |
getClassLoader()
返回 ClassLoader 实例,对于想要获取 ResourceLoader 使用的 ClassLoader 用户来说,可以直接调用该方法来获取
DefaultResourceLoader 是 ResourceLoader 的默认实现。ResourceLoader 中最核心的方法为 getResource(),它根据提供的 location 返回相应的 Resource,而 DefaultResourceLoader 对该方法提供了核心实现(它的其它子类都没有提供覆盖该方法,所以可以断定ResourceLoader 的资源加载策略就封装 DefaultResourceLoader中)
//获取Resource的具体实现类实例
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
//ProtocolResolver ,用户自定义协议资源解决策略
for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
//如果是以/开头,则构造ClassPathContextResource返回
if (location.startsWith("/")) {
return getResourceByPath(location);
}
//若以classpath:开头,则构造 ClassPathResource 类型资源并返回,在构造该资源时,
// 通过 getClassLoader()获取当前的 ClassLoader
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
//构造 URL ,尝试通过它进行资源定位,若没有抛出 MalformedURLException 异常,
// 则判断是否为 FileURL , 如果是则构造 FileUrlResource 类型资源,否则构造 UrlResource。
// 若在加载过程中抛出 MalformedURLException 异常,
// 则委派 getResourceByPath() 实现资源定位加载
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);
}
}
}
FileSystemResourceLoader, 它继承 DefaultResourceLoader 且覆写了 getResourceByPath(String),使之从文件系统加载资源并以 FileSystemResource 类型返回。能够更加精确的判断FileSystemResource文件类型资源
public class FileSystemResourceLoader extends DefaultResourceLoader {
/**
* Resolve resource paths as file system paths.
* Note: Even if a given path starts with a slash, it will get
* interpreted as relative to the current VM working directory.
* @param path the path to the resource
* @return the corresponding Resource handle
* @see FileSystemResource
* @see org.springframework.web.context.support.ServletContextResourceLoader#getResourceByPath
*/
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemContextResource(path);
}
/**
* FileSystemResource that explicitly expresses a context-relative path
* through implementing the ContextResource interface.
*/
private static class FileSystemContextResource extends FileSystemResource implements ContextResource {
public FileSystemContextResource(String path) {
super(path);
}
@Override
public String getPathWithinContext() {
return getPath();
}
}
}
ClassRelativeResourceLoader继承自DefaultResourceLoader,并重写了getResourceByPath(String path) 。
ClassRelativeResourceLoader返回的是ClassRelativeContextResource对象,ClassRelativeContextResource对象表示上下文相对路径 .
例如包结构:
com.bushro.web
LoginController
test.xml
如果需要加载test.xml文件,咱们就可以在LoginController这么写:
public String loginPage() throws IOException {
ResourceLoader resourceLoader=new ClassRelativeResourceLoader(this.getClass());
Resource resource=resourceLoader.getResource("test.xml");
System.out.println(resource.getFile().getPath());
return "index";
}
该类的扩展功能是可以从Servlet上下文的根目录加载资源。
public interface ResourcePatternResolver extends ResourceLoader {
/**
* 在所有根目录下搜索文件的伪URL的前缀
* 与ResourceLoader中classpath不同的地方在于,此前缀会在所有的JAR包的根目录下搜索指定文件。
*/
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
/**
* 返回指定路径下所有的资源对象。
* 返回的对象集合应该有Set的语义,也就是说,对于同一个资源,只应该返回一个资源对象
*/
Resource[] getResources(String locationPattern) throws IOException;
}