天天看点

Spring之ResourceResourceResourceLoader结束

 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的接口有子接口和几个实现类,其基本结构继承如下:

Spring之ResourceResourceResourceLoader结束

首先是包含两个子接口WritableResource 扩展了资源的可写性,ContextResource扩展,在某个上下文中读取资源。

所有的具体实现类都继承AbstractResource。从名称上可以明显的看出,不同的实现类负责不同形式的资源。

其中FileSystemResource extendsAbstractResource implements WritableResource ,这是一种很常见的扩展方式。

ResourceLoader

Spring提供了很多个不同类型的Resource,ResourceLoader则负责根据资源描述符(通常为字符串(数组),可带通配符),根据不同的情况,建立不同的Resource实例。

Spring之ResourceResourceResourceLoader结束

接口定义如下:

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的基本实现)都没有列出来。