Spring之所以抽象Resource接口,是因为传统的java使用URL和标准的handler来处置资源。但是有个局限是不能直接读取classpath下的资源。因此Spring定义了一套接口。这套接口分为两部分,Resource和ResourceLoader,其中是对资源的抽象,后者负责加载资源。Spring的ApplicationContext实现了ResourceLoader接口。
Resource
首先看Resource的接口定义。
public interface Resource extends InputStreamSource {
boolean exists();
boolean isReadable();
/**
* Return whether this resource represents a handle with an open
* stream. If true, the InputStream cannot be read multiple times,
* and must be read and closed to avoid resource leaks.
* <p>Will be {@code false} for typical resource descriptors.
*/
boolean isOpen();
/**
* Return a URL handle for this resource.
* @throws IOException if the resource cannot be resolved as URL,
* i.e. if the resource is not available as descriptor
*/
URL getURL() throws IOException;
/**
* Return a URI handle for this resource.
* @throws IOException if the resource cannot be resolved as URI,
* i.e. if the resource is not available as descriptor
*/
URI getURI() throws IOException;
File getFile() throws IOException;
long contentLength() throws IOException;
*/
long lastModified() throws IOException;
/**
* Create a resource relative to this resource.
* @param relativePath the relative path (relative to this resource)
* @return the resource handle for the relative resource
* @throws IOException if the relative resource cannot be determined
*/
Resource createRelative(String relativePath) throws IOException;
String getFilename();
String getDescription();
}
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
以下接口简单明了,唯一需要注意的就是方法isOpen(),返回true表示此资源可以被多次读取,且需要使用者关闭。典型实现返回的都是false.
Resource的接口有子接口和几个实现类,其基本结构继承如下:
首先是包含两个子接口WritableResource 扩展了资源的可写性,ContextResource扩展,在某个上下文中读取资源。
所有的具体实现类都继承AbstractResource。从名称上可以明显的看出,不同的实现类负责不同形式的资源。
其中FileSystemResource extendsAbstractResource implements WritableResource ,这是一种很常见的扩展方式。
ResourceLoader
Spring提供了很多个不同类型的Resource,ResourceLoader则负责根据资源描述符(通常为字符串(数组),可带通配符),根据不同的情况,建立不同的Resource实例。
接口定义如下:
public interface ResourceLoader {
/** Pseudo URL prefix for loading from the class path: "classpath:" */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
Resource getResource(String location);
ClassLoader getClassLoader();
}
定义了加载资源的方法,并暴露ResourceLoader的类加载器,其实现类和子接口集成结构如下:
我们使用的ApplicationContex都继承ResourceLoader,其中DefaultResourceLoader的实现getResource如下,分三种情况进行资源的加载:
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
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 new UrlResource(url);
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
下面看在Spring构建ApplicationContext的时候,是如何使用ResourceLoader的,这里单摘出加载xml配置的部分代码。在AbstractBeanDefinitionReader以下方法:
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
//支持通配符
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
。。。。。
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
。。。。
}
return loadCount;
}
}
@Override
//这里返回的就是ApplicationContext
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
结束
本文简单的分析了Spring的Resource框架,并单独摘出了Spring在加载bean定义的时使用Resource的代码,理解这点,为了解spring容器的启动过程打下一个小基础。本文依照spring源码写成,由于此部分相当简单,故很多部分(如AbstractResource的基本实现)都没有列出来。