Java中处理资源的核心接口是InputStream和OutputStream,但是用起来不是特别方便,spring在此基础上做了进一步的封装,形成了以Resource接口为核心的一套资源体系。
首先看下Resource接口:
public interface Resource extends InputStreamSource {
  boolean exists();
  boolean isReadable();
  boolean isOpen();
  URL getURL() throws IOException;
  URI getURI() throws IOException;
  File getFile() throws IOException;
  long contentLength() throws IOException;
  long lastModified() throws IOException;
  Resource createRelative(String relativePath) throws IOException;
  String getFilename();
  String getDescription();
}对于不同类型的资源,其实现是不同的,比如处理classpath下的资源就和处理文件系统下的资源方式不同。但是其行为都可以统一抽象为上面的接口。
下面分别看几个调用的例子:
Properties properties1 = new Properties();
Resource r1 = new ClassPathResource("a.properties");
properties1.load(r1.getInputStream());
System.out.println(properties1.getProperty("name"));
Resource r2 = new FileSystemResource("/Users/miracle/Desktop/1.properties");
Properties properties2 = new Properties();
properties2.load(r2.getInputStream());
System.out.println(properties2.getProperty("name"));
Resource r3 = new UrlResource("http://localhost:8080/test");
System.out.println(IOUtils.toString(r3.getInputStream(), "utf-8"));
分别是基于classpath、文件系统以及网络url的三种实现。对于读取而言,都是先构造一个Resource实例,然后拿到其内部的InputStream,然后读取。
其实,基于上面的方式,已经可以比较容易的获取资源了,但是spring提供了更加强大的功能,ResourceLoader接口:
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接口实例,我们可以无需关心Resource具体的实现类型,只要按照ResourceLoader的约定传入不同的资源路径,ResourceLoader会根据资源路径的格式做出相应的判断,查找对应类型的资源。
该接口的默认实现是DefaultResourceLoader,可以看下其getResource方法的实现:
public Resource getResource(String location) {
    Assert.notNull(location, "Location must not be null");
    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);
      }
    }
  }首先判定传入的路径参数是否以“classpath:”开头,如果是的话,就按照classpathresource的方式来加载资源,否则的话按照urlresource的方式加载(注意!!!该模式既可以处理files:xxx格式的文件资源,也可以处理url的),如果还不行,仍然以classpath的方式解析。
如上方式是不支持ant风格的路径的,也就是一次只能匹配一个资源。所以,spring中常用的是其子接口:ResourcePatternResolver
该接口增加了Ant风格的资源路径,可以返回多个。实现类为:PathMatchingResourcePatternResolver。
classpath*:xxx
classpath:xxx
前者可以匹配classpath下所有符合条件的资源,后者只能匹配一个。
另外,也可以在路径中加入通配符:
如files:/*/a.txt等。
关于Ant路径,这里就不详述了,可以参考:javascript:void(0) ?:一个字符
*:任意0或多个字符
**:任意0或多级目录
最后看下其核心方法:
public Resource[] getResources(String locationPattern) throws IOException {
    Assert.notNull(locationPattern, "Location pattern must not be null");
    if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
      // a class path resource (multiple resources for same name possible)
      if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
        // a class path resource pattern
        return findPathMatchingResources(locationPattern);
      }
      else {
        // all class path resources with the given name
        return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
      }
    }
    else {
      // Only look for a pattern after a prefix here
      // (to not get fooled by a pattern symbol in a strange prefix).
      int prefixEnd = locationPattern.indexOf(":") + 1;
      if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
        // a file pattern
        return findPathMatchingResources(locationPattern);
      }
      else {
        // a single resource with the given name
        return new Resource[] {getResourceLoader().getResource(locationPattern)};
      }
    }
  }










