2.1 引言

本节主要介绍Spring如何处理资源,以及如何在Spring使用资源。包含

由于java.net.URL 类和各种 URL 标准实现并不满足对低级资源的所有访问,所以需要更高一级的抽象。

2.2 资源接口

Spring 的 Resource 接口旨在成为一个更有能力的接口,用于抽象对底层资源的访问。

  1. public interface Resource extends InputStreamSource {
  2. // 返回true/false。表示此资源是否物理存在
  3. boolean exists();
  4. // 返回true/false。表示此资源是否代表具有打开流的句柄。为true表示不能多次读取InputStream。
  5. // 必须读一次关一次,从而避免资源泄漏。通用的资源通常返回false,InputStreamResource除外。
  6. boolean isOpen();
  7. URL getURL() throws IOException;
  8. File getFile() throws IOException;
  9. Resource createRelative(String relativePath) throws IOException;
  10. String getFilename();
  11. String getDescription();
  12. }

扩展了 InputStreamSource 接口

public interface InputStreamSource {
    // 定位并撕开资源,返回一个InputStream以便从该资源读取。调用者有必要关闭流
    InputStream getInputStream() throws IOException;
}

2.3 内置资源的实现

包含以下资源的实现:

  • UrlResource
  • ClassPathResource
  • FileSystemResource
  • ServletContextResource
  • InputStreamResource
  • ByteArrayResource

    2.3.1 UrlResource

    UrlResource 包装了一个 java.net.URL ,可以用来访问通常可以通过 URL 访问的任何对象,比如文件、 HTTP 目标、 FTP 目标等。所有 URL 都具有标准化的 String 表示形式,这样就可以使用适当的标准化前缀来表示一种 URL 类型与另一种 URL 类型。这包括文件: 用于访问文件系统路径,HTTP: 用于通过 HTTP 协议访问资源,FTP: 用于通过 FTP 访问资源,以及其他。
    一个 UrlResource 是由 Java 代码通过显式地使用 UrlResource 构造函数创建的,但是通常在调用一个 API 方法时隐式地创建,该方法采用一个 String 参数来表示一个路径。对于后一种情况,javabean PropertyEditor 最终决定创建哪种类型的 Resource

    2.3.2 ClassPathResource

    此类表示应该从 类路径 获取的资源。
    It uses either the thread context class loader, a given class loader, or a given class for loading resources.

    2.3.3 FileSystemResource

    这是 java.io.Filejava.nio.file.Path 句柄的Resource实现。 它支持作为文件和URL的解析。

    2.3.4 ServletContextResource

    这是 ServletContext 资源的一个资源实现,它解释了相关 web 应用程序根目录中的相对路径。

    2.3.5 InputStreamResource

    InputStreamResource 是给定 InputStream 的 Resource 实现。只有在没有特定的 Resource 实现可用的情况下才应该使用它。特别是,如果可能的话,最好使用 ByteArrayResource 或任何基于文件的 Resource 实现。

    2.3.6 ByteArrayResource

    这是一个给定字节数组的资源实现,它为给定的字节数组创建一个 ByteArrayInputStream
    对于从任何给定的字节数组加载内容,而不必求助于一次性使用的 InputStreamResource,它非常有用。

    2.3.7 ResourceLoader

    ResourceLoader 接口意味着由可以返回(即加载) Resource 实例的对象实现。下面的清单显示了 ResourceLoader 接口定义:
    public interface ResourceLoader {
      Resource getResource(String location);
    }
    
    所有应用程序上下文都实现了 ResourceLoader 接口。因此,所有应用程序上下文都可用于获取 Resource 实例。
Prefix Example Explanation
classpath: classpath:com/myapp/config.xml Loaded from the classpath.
file: [file:///data/config.xml]() Loaded as a URL from the filesystem. See also FileSystemResource Caveats.
http: [https://myserver/logo.png](https://myserver/logo.png) Loaded as a URL.
(none) /data/config.xml Depends on the underlying ApplicationContext.

2.5 ResourceLoaderAware接口

ResourceLoaderAware 接口是一个特殊的回调接口,用于标识希望通过 ResourceLoader 引用提供的组件。

public interface ResourceLoaderAware {
    void setResourceLoader(ResourceLoader resourceLoader);
}

2.6 Resources作为依赖

如果Bean本身将通过某种动态过程来确定并提供资源路径,那么对于Bean来说,使用ResourceLoader 接口加载资源可能是有意义的。 例如,考虑加载某种模板,其中所需的特定资源取决于用户的角色。 如果资源是静态的,则有必要完全消除对ResourceLoader接口的使用,让Bean公开所需的Resource属性,并期望将其注入其中。
然后注入这些属性很简单,因为所有应用程序上下文都注册并使用一个特殊的 JavaBeans PropertyEditor,它可以将 String 路径转换为 Resource 对象。因此,如果 myBean 有一个类型为 Resource 的模板属性,那么可以为该资源配置一个简单的字符串,如下面的示例所示:

<bean id="myBean" class="...">
    <property name="template" value="some/resource/path/myTemplate.txt"/>
</bean>

2.7 应用程序上下文和资源路径

2.7.1 构造应用程序上下文

应用程序上下文构造函数(针对特定的应用程序上下文类型)通常将字符串或字符串数组作为资源的位置路径,例如构成上下文定义的 XML 文件。

2.7.2. 应用上下文构造函数资源路径中的通配符

应用程序上下文构造函数值中的资源路径可以是简单路径(如先前所示),每个路径都具有到目标Resource 的一对一映射,或者可以包含特殊的 classpath *: 前缀或内部 Ant 样式的正则表达式(通过使用Spring的PathMatcher 实用程序进行匹配)。 后者都是有效的通配符。
这种机制的一种用途是当您需要进行组件样式的应用程序组装时,所有组件都可以将上下文定义片段“发布”到一个众所周知的位置路径,并且当使用前缀为classpath*:的相同路径创建最终应用程序上下文时,将自动拾取所有组件片段。注意,这种通配特定于应用程序上下文构造函数中资源路径的使用(或者当您直接使用 PathMatcher 实用程序类层次结构时) ,并在构造时解析。它与资源类型本身没有任何关系。不能使用类路径 * : 前缀构造实际的资源,因为资源一次只能指向一个资源。

Ant-style Patterns

/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/**/applicationContext.xml

当路径位置包含 ant 样式的模式时,解析程序遵循一个更复杂的过程来尝试解析通配符。它为直到最后一个非通配符段的路径生成一个 Resource,并从中获得一个 URL。文件从中获取,并通过遍历文件系统来解析通配符。

classpath*:

ApplicationContext ctx =
    new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");

这个特殊的前缀指定必须获得所有与给定名称匹配的类路径资源(在内部,这实际上是通过调用 ClassLoader.getResources (…)) ,然后合并以形成最终的应用程序上下文定义。

Spring检索路径的能力来自JDK的 ClassLoader.getResources() 方法。
Such a resource may be in only one location, but when a path such as the preceding example is used to try to resolve it, the resolver works off the (first) URL returned by getResource("com/mycompany");. If this base package node exists in multiple classloader locations, the actual end resource may not be there. Therefore, in such a case you should prefer using classpath*: with the same Ant-style pattern, which searches all class path locations that contain the root package.