2. 资源(Resources)

预计阅读时间: 50 分钟

This chapter covers how Spring handles resources and how you can work with resources in Spring. It includes the following topics:
本章涵盖 Spring 如何处理资源以及如何在 Spring 中与资源一起工作的内容。包括以下主题:

(#resources-introduction)2.1. Introduction 2.1. 简介

Java’s standard java.net.URL class and standard handlers for various URL prefixes, unfortunately, are not quite adequate enough for all access to low-level resources. For example, there is no standardized URL implementation that may be used to access a resource that needs to be obtained from the classpath or relative to a ServletContext. While it is possible to register new handlers for specialized URL prefixes (similar to existing handlers for prefixes such as http:), this is generally quite complicated, and the URL interface still lacks some desirable functionality, such as a method to check for the existence of the resource being pointed to.
Java 的标准 java.net.URL 类和针对各种 URL 前缀的标准处理器,遗憾的是,对于所有对低级资源的访问来说并不完全足够。例如,没有标准化的 URL 实现可以用来访问需要从类路径或相对于 ServletContext .获取的资源。虽然可以为专门的 URL 前缀注册新的处理器(类似于现有针对如 http: 前缀的处理器),但这通常相当复杂, URL 接口仍然缺少一些期望的功能,例如检查所指向的资源是否存在的方法。

(#resources-resource)2.2. TheResource Interface 2.2.Resource 接口

Spring’s Resource interface located in the org.springframework.core.io. package is meant to be a more capable interface for abstracting access to low-level resources. The following listing provides an overview of the Resource interface. See the Resource javadoc for further details.
春季的 Resource 接口位于 org.springframework.core.io. 包中,旨在成为一个更强大的接口,用于抽象访问低级资源。以下列表提供了 Resource 接口的概述。请参阅 Resource javadoc 以获取更多详细信息。

public interface Resource extends InputStreamSource { boolean exists(); boolean isReadable(); boolean isOpen(); boolean isFile(); URL getURL() throws IOException; URI getURI() throws IOException; File getFile() throws IOException; ReadableByteChannel readableChannel() throws IOException; long contentLength() throws IOException; long lastModified() throws IOException; Resource createRelative(String relativePath) throws IOException; String getFilename(); String getDescription(); }

As the definition of the Resource interface shows, it extends the InputStreamSource interface. The following listing shows the definition of the InputStreamSource interface:

public interface InputStreamSource { InputStream getInputStream() throws IOException; }

Some of the most important methods from the Resource interface are:
一些来自 Resource 接口的最重要方法包括:

  • getInputStream(): Locates and opens the resource, returning an InputStream for reading from the resource. It is expected that each invocation returns a fresh InputStream. It is the responsibility of the caller to close the stream.
    getInputStream() :定位并打开资源,返回一个 InputStream 以从资源中读取。预期每次调用都返回一个新的 InputStream 。关闭流是调用者的责任。

  • exists(): Returns a boolean indicating whether this resource actually exists in physical form.
    exists() :返回一个 boolean ,表示此资源是否以实物形式实际存在。

  • isOpen(): Returns a boolean indicating whether this resource represents a handle with an open stream. If true, the InputStream cannot be read multiple times and must be read once only and then closed to avoid resource leaks. Returns false for all usual resource implementations, with the exception of InputStreamResource.
    isOpen() :返回一个 boolean ,表示此资源是否表示一个具有打开流的句柄。如果 true ,则 InputStream 不能多次读取,必须只读取一次然后关闭,以避免资源泄漏。对于所有常规资源实现,除了 InputStreamResource 之外,都返回 false

  • getDescription(): Returns a description for this resource, to be used for error output when working with the resource. This is often the fully qualified file name or the actual URL of the resource.
    getDescription() :返回该资源的描述,用于处理资源时的错误输出。这通常是完全限定的文件名或资源的实际 URL。

Other methods let you obtain an actual URL or File object representing the resource (if the underlying implementation is compatible and supports that functionality).
其他方法允许您获取一个实际表示资源的 URLFile 对象(如果底层实现兼容且支持该功能)。

Some implementations of the Resource interface also implement the extended WritableResource interface for a resource that supports writing to it.
某些对 Resource 接口的实现也实现了对支持写入的资源扩展的 WritableResource 接口。

Spring itself uses the Resource abstraction extensively, as an argument type in many method signatures when a resource is needed. Other methods in some Spring APIs (such as the constructors to various ApplicationContext implementations) take a String which in unadorned or simple form is used to create a Resource appropriate to that context implementation or, via special prefixes on the String path, let the caller specify that a specific Resource implementation must be created and used.
Spring 本身广泛使用 Resource 抽象,作为许多方法签名中的参数类型,当需要资源时。某些 Spring API 中的其他方法(如各种 ApplicationContext 实现的构造函数)接受一个 String ,在未装饰或简单形式下,用于创建适合该上下文实现的 Resource ,或者通过 String 路径上的特殊前缀,让调用者指定必须创建并使用特定的 Resource 实现。

While the Resource interface is used a lot with Spring and by Spring, it is actually very convenient to use as a general utility class by itself in your own code, for access to resources, even when your code does not know or care about any other parts of Spring.
尽管 Resource 接口在 Spring 及其与 Spring 一起使用时非常普遍,但实际上,将其作为通用工具类单独用于自己的代码中非常方便,用于访问资源,即使您的代码不知道或不在乎 Spring 的任何其他部分。
While this couples your code to Spring, it really only couples it to this small set of utility classes, which serves as a more capable replacement for URL and can be considered equivalent to any other library you would use for this purpose.
虽然这使您的代码与 Spring 耦合,但实际上它仅与这一小套实用类耦合,这可以作为 URL 的更强大替代品,可以被认为是用于此目的的任何其他库的等价物。

The Resource abstraction does not replace functionality. It wraps it where possible. For example, a UrlResource wraps a URL and uses the wrapped URL to do its work.
Resource 抽象不替代功能。它尽可能封装它。例如,一个 UrlResource 封装了一个 URL,并使用封装的 URL 来完成其工作。

(#resources-implementations)2.3. Built-inResource Implementations

2.3. 内置 Resource 实现

Spring includes several built-in Resource implementations:
Spring 包含几个内置的 Resource 实现:

For a complete list of Resource implementations available in Spring, consult the "All Known Implementing Classes" section of the Resource javadoc.
为了获取 Spring 中可用的 Resource 实现列表的完整列表,请参阅 Resource javadoc 中的“所有已知实现类”部分。

(#resources-implementations-urlresource)2.3.1.UrlResource

UrlResource wraps a java.net.URL and can be used to access any object that is normally accessible with a URL, such as files, an HTTPS target, an FTP target, and others. All URLs have a standardized String representation, such that appropriate standardized prefixes are used to indicate one URL type from another. This includes file: for accessing filesystem paths, https: for accessing resources through the HTTPS protocol, ftp: for accessing resources through FTP, and others.
UrlResource 包装了 java.net.URL ,可以用来访问任何通常可以通过 URL 访问的对象,例如文件、HTTPS 目标、FTP 目标等。所有 URL 都有标准化的 String 表示形式,以便使用适当的标准化前缀来指示一种 URL 类型与另一种类型。这包括 file: 用于访问文件系统路径, https: 用于通过 HTTPS 协议访问资源, ftp: 用于通过 FTP 访问资源,以及其他。

A UrlResource is created by Java code by explicitly using the UrlResource constructor but is often created implicitly when you call an API method that takes a String argument meant to represent a path. For the latter case, a JavaBeans PropertyEditor ultimately decides which type of Resource to create. If the path string contains a well-known (to property editor, that is) prefix (such as classpath:), it creates an appropriate specialized Resource for that prefix. However, if it does not recognize the prefix, it assumes the string is a standard URL string and creates a UrlResource.
一个 UrlResource 通过 Java 代码显式使用 UrlResource 构造函数创建,但在调用一个接受 String 参数(用于表示路径)的 API 方法时通常隐式创建。对于后一种情况,JavaBeans PropertyEditor 最终决定创建哪种类型的 Resource 。如果路径字符串包含一个已知的(对属性编辑器而言)前缀(例如 classpath: ),它将为该前缀创建一个适当的专用 Resource 。然而,如果它不识别前缀,它假定该字符串是一个标准的 URL 字符串,并创建一个 UrlResource

(#resources-implementations-classpathresource)2.3.2.ClassPathResource

This class represents a resource that should be obtained from the classpath. It uses either the thread context class loader, a given class loader, or a given class for loading resources.

This Resource implementation supports resolution as a java.io.File if the class path resource resides in the file system but not for classpath resources that reside in a jar and have not been expanded (by the servlet engine or whatever the environment is) to the filesystem. To address this, the various Resource implementations always support resolution as a java.net.URL.

A ClassPathResource is created by Java code by explicitly using the ClassPathResource constructor but is often created implicitly when you call an API method that takes a String argument meant to represent a path. For the latter case, a JavaBeans PropertyEditor recognizes the special prefix, classpath:, on the string path and creates a ClassPathResource in that case.

(#resources-implementations-filesystemresource)2.3.3.FileSystemResource

This is a Resource implementation for java.io.File handles. It also supports java.nio.file.Path handles, applying Spring’s standard String-based path transformations but performing all operations via the java.nio.file.Files API. For pure java.nio.path.Path based support use a PathResource instead. FileSystemResource supports resolution as a File and as a URL.
这是一个针对 java.io.File 处理的 Resource 实现。它还支持 java.nio.file.Path 处理,应用 Spring 的标准基于 String 的路径转换,但所有操作都通过 java.nio.file.Files API 执行。对于纯 java.nio.path.Path 支持,请使用 PathResourceFileSystemResource 支持作为 FileURL 的解析。

(#resources-implementations-pathresource)2.3.4.PathResource

This is a Resource implementation for java.nio.file.Path handles, performing all operations and transformations via the Path API. It supports resolution as a File and as a URL and also implements the extended WritableResource interface. PathResource is effectively a pure java.nio.path.Path based alternative to FileSystemResource with different createRelative behavior.
这是一个针对 java.nio.file.Path 处理的 Resource 实现,通过 Path API 执行所有操作和转换。它支持以 FileURL 的分辨率,并实现了扩展的 WritableResource 接口。 PathResource 是一个基于 java.nio.path.Path 的纯 FileSystemResource 替代方案,具有不同的 createRelative 行为。

(#resources-implementations-servletcontextresource)2.3.5.ServletContextResource

This is a Resource implementation for ServletContext resources that interprets relative paths within the relevant web application’s root directory.
这是一个针对 ServletContext 资源的 Resource 实现,它解释了相关 Web 应用程序根目录内的相对路径。

It always supports stream access and URL access but allows java.io.File access only when the web application archive is expanded and the resource is physically on the filesystem.
它始终支持流访问和 URL 访问,但仅在 Web 应用程序存档展开且资源实际位于文件系统上时才允许 java.io.File 访问。
Whether or not it is expanded and on the filesystem or accessed directly from the JAR or somewhere else like a database (which is conceivable) is actually dependent on the Servlet container.
是否扩展以及是否在文件系统中或在 JAR 文件或数据库(这是可以想象的)中直接访问,实际上取决于 Servlet 容器。

(#resources-implementations-inputstreamresource)2.3.6.InputStreamResource

An InputStreamResource is a Resource implementation for a given InputStream. It should be used only if no specific Resource implementation is applicable. In particular, prefer ByteArrayResource or any of the file-based Resource implementations where possible.
一个 InputStreamResource 是针对特定 InputStreamResource 实现。只有在没有适用的特定 Resource 实现时才应使用。特别是,如果可能,优先选择 ByteArrayResource 或任何基于文件的 Resource 实现。

In contrast to other Resource implementations, this is a descriptor for an already-opened resource. Therefore, it returns true from isOpen(). Do not use it if you need to keep the resource descriptor somewhere or if you need to read a stream multiple times.
与其他 Resource 实现相比,这是一个已打开资源的描述符。因此,它从 isOpen() 返回 true 。如果需要将资源描述符保存在某处或需要多次读取流,则不要使用它。

(#resources-implementations-bytearrayresource)2.3.7.ByteArrayResource

This is a Resource implementation for a given byte array. It creates a ByteArrayInputStream for the given byte array.
这是一个针对给定字节数组的 Resource 实现。它为给定的字节数组创建一个 ByteArrayInputStream

It is useful for loading content from any given byte array without having to resort to a single-use InputStreamResource.
它适用于从任何给定的字节数组中加载内容,无需求助于单次使用的 InputStreamResource

(#resources-resourceloader)2.4. TheResourceLoader Interface 2.4.ResourceLoader 接口

The ResourceLoader interface is meant to be implemented by objects that can return (that is, load) Resource instances. The following listing shows the ResourceLoader interface definition:
ResourceLoader 接口旨在由可以返回(即加载) Resource 实例的对象实现。以下列表显示了 ResourceLoader 接口的定义:

public interface ResourceLoader { Resource getResource(String location); ClassLoader getClassLoader(); }

All application contexts implement the ResourceLoader interface. Therefore, all application contexts may be used to obtain Resource instances.
所有应用程序上下文都实现了 ResourceLoader 接口。因此,所有应用程序上下文都可以用来获取 Resource 实例。

When you call getResource() on a specific application context, and the location path specified doesn’t have a specific prefix, you get back a Resource type that is appropriate to that particular application context. For example, assume the following snippet of code was run against a ClassPathXmlApplicationContext instance:
当您在特定应用程序上下文中调用 getResource() 时,如果指定的位置路径没有特定前缀,您将返回一个适用于该特定应用程序上下文的 Resource 类型。例如,假设以下代码片段是在一个 ClassPathXmlApplicationContext 实例上运行的:

Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
val template = ctx.getResource("some/resource/path/myTemplate.txt")

Against a ClassPathXmlApplicationContext, that code returns a ClassPathResource. If the same method were run against a FileSystemXmlApplicationContext instance, it would return a FileSystemResource. For a WebApplicationContext, it would return a ServletContextResource. It would similarly return appropriate objects for each context.

As a result, you can load resources in a fashion appropriate to the particular application context.

On the other hand, you may also force ClassPathResource to be used, regardless of the application context type, by specifying the special classpath: prefix, as the following example shows:

Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
val template = ctx.getResource("classpath:some/resource/path/myTemplate.txt")

Similarly, you can force a UrlResource to be used by specifying any of the standard java.net.URL prefixes. The following examples use the file and https prefixes:

Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
val template = ctx.getResource("file:///some/resource/path/myTemplate.txt")
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
val template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt")

The following table summarizes the strategy for converting String objects to Resource objects:
以下表格总结了将 String 对象转换为 Resource 对象的策略:

Table 10. Resource strings
表 10. 资源字符串

Prefix 前缀

Example 示例

Explanation 说明

classpath: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.
作为文件系统中的 URL 加载。另见 FileSystemResource 注意事项。

https:. https:

https://myserver/logo.png

Loaded as a URL.
已加载为 URL

(none)(无)

/data/config.xml

Depends on the underlying ApplicationContext.
取决于底层 ApplicationContext

(#resources-resourcepatternresolver)2.5. TheResourcePatternResolver Interface 2.5.ResourcePatternResolver 接口

The ResourcePatternResolver interface is an extension to the ResourceLoader interface which defines a strategy for resolving a location pattern (for example, an Ant-style path pattern) into Resource objects.
ResourcePatternResolver 接口是对 ResourceLoader 接口的扩展,它定义了一种将位置模式(例如,Ant 风格的路径模式)解析为 Resource 对象的策略。

public interface ResourcePatternResolver extends ResourceLoader { String CLASSPATH_ALL_URL_PREFIX = "classpath*:"; Resource getResources(String locationPattern) throws IOException; }

As can be seen above, this interface also defines a special classpath*: resource prefix for all matching resources from the class path. Note that the resource location is expected to be a path without placeholders in this case — for example, classpath*:/config/beans.xml. JAR files or different directories in the class path can contain multiple files with the same path and the same name. See Wildcards in Application Context Constructor Resource Paths and its subsections for further details on wildcard support with the classpath*: resource prefix.
如上所示,此界面还为类路径中所有匹配的资源定义了一个特殊的 classpath*: 资源前缀。请注意,在这种情况下,资源位置预期为一个不带占位符的路径——例如, classpath*:/config/beans.xml 。JAR 文件或类路径中的不同目录可以包含具有相同路径和名称的多个文件。有关 classpath*: 资源前缀的通配符支持的详细信息,请参阅应用程序上下文构造函数资源路径中的通配符及其子部分。

A passed-in ResourceLoader (for example, one supplied via ResourceLoaderAware semantics) can be checked whether it implements this extended interface too.
传入的 ResourceLoader (例如,通过 ResourceLoaderAware 语义提供的)也可以检查它是否实现了这个扩展接口。

PathMatchingResourcePatternResolver is a standalone implementation that is usable outside an ApplicationContext and is also used by ResourceArrayPropertyEditor for populating Resource bean properties. PathMatchingResourcePatternResolver is able to resolve a specified resource location path into one or more matching Resource objects. The source path may be a simple path which has a one-to-one mapping to a target Resource, or alternatively may contain the special classpath*: prefix and/or internal Ant-style regular expressions (matched using Spring’s org.springframework.util.AntPathMatcher utility). Both of the latter are effectively wildcards.
PathMatchingResourcePatternResolver 是一个独立实现,可以在 ApplicationContext 之外使用,也被 ResourceArrayPropertyEditor 用于填充 Resource 的属性。 PathMatchingResourcePatternResolver 能够将指定的资源位置路径解析为一个或多个匹配的 Resource 对象。源路径可能是一个简单的路径,它与目标 Resource 有一对一的映射,或者可以包含特殊的 classpath*: 前缀和/或内部 Ant 风格的正则表达式(使用 Spring 的 org.springframework.util.AntPathMatcher 工具进行匹配)。后两者都是有效的通配符。

The default ResourceLoader in any standard ApplicationContext is in fact an instance of PathMatchingResourcePatternResolver which implements the ResourcePatternResolver interface. The same is true for the ApplicationContext instance itself which also implements the ResourcePatternResolver interface and delegates to the default PathMatchingResourcePatternResolver.
任何标准 ApplicationContext 中的默认 ResourceLoader 实际上是实现了 ResourcePatternResolver 接口的 PathMatchingResourcePatternResolver 的一个实例。对于自身也实现了 ResourcePatternResolver 接口并委托给默认的 PathMatchingResourcePatternResolverApplicationContext 实例来说,也是如此。

(#resources-resourceloaderaware)2.6. TheResourceLoaderAware Interface 2.6.ResourceLoaderAware 接口

The ResourceLoaderAware interface is a special callback interface which identifies components that expect to be provided a ResourceLoader reference. The following listing shows the definition of the ResourceLoaderAware interface:
ResourceLoaderAware 接口是一个特殊的回调接口,用于标识期望提供 ResourceLoader 引用的组件。以下列表显示了 ResourceLoaderAware 接口的定义:

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

When a class implements ResourceLoaderAware and is deployed into an application context (as a Spring-managed bean), it is recognized as ResourceLoaderAware by the application context. The application context then invokes setResourceLoader(ResourceLoader), supplying itself as the argument (remember, all application contexts in Spring implement the ResourceLoader interface).
当一个类实现 ResourceLoaderAware 并将其部署到应用程序上下文中(作为 Spring 管理的 bean),应用程序上下文将其识别为 ResourceLoaderAware 。然后应用程序上下文调用 setResourceLoader(ResourceLoader) ,并将自身作为参数传递(记住,Spring 中的所有应用程序上下文都实现了 ResourceLoader 接口)。

Since an ApplicationContext is a ResourceLoader, the bean could also implement the ApplicationContextAware interface and use the supplied application context directly to load resources. However, in general, it is better to use the specialized ResourceLoader interface if that is all you need. The code would be coupled only to the resource loading interface (which can be considered a utility interface) and not to the whole Spring ApplicationContext interface.
由于一个 ApplicationContext 是一个 ResourceLoader ,因此 Bean 也可以实现 ApplicationContextAware 接口并直接使用提供的应用程序上下文来加载资源。然而,通常情况下,如果您只需要这些,使用专门的 ResourceLoader 接口会更好。代码将仅与资源加载接口(可以视为工具接口)耦合,而不是与整个 Spring ApplicationContext 接口耦合。

In application components, you may also rely upon autowiring of the ResourceLoader as an alternative to implementing the ResourceLoaderAware interface. The traditional constructor and byType autowiring modes (as described in Autowiring Collaborators) are capable of providing a ResourceLoader for either a constructor argument or a setter method parameter, respectively. For more flexibility (including the ability to autowire fields and multiple parameter methods), consider using the annotation-based autowiring features. In that case, the ResourceLoader is autowired into a field, constructor argument, or method parameter that expects the ResourceLoader type as long as the field, constructor, or method in question carries the @Autowired annotation. For more information, see Using @Autowired.
在应用程序组件中,您还可以将 ResourceLoader 的自动装配作为实现 ResourceLoaderAware 接口的替代方案。传统的 constructorbyType 自动装配模式(如《自动装配协作器》中所述)能够分别为构造函数参数或 setter 方法参数提供 ResourceLoader 。为了获得更多灵活性(包括自动装配字段和多参数方法的能力),请考虑使用基于注解的自动装配功能。在这种情况下, ResourceLoader 将被自动装配到期望 ResourceLoader 类型的字段、构造函数或方法参数中,只要相关的字段、构造函数或方法具有 @Autowired 注解。有关更多信息,请参阅使用 @Autowired

To load one or more Resource objects for a resource path that contains wildcards or makes use of the special classpath*: resource prefix, consider having an instance of ResourcePatternResolver autowired into your application components instead of ResourceLoader.
为了加载一个或多个 Resource 对象,这些对象位于包含通配符或使用特殊 classpath*: 资源前缀的资源路径中,考虑将 ResourcePatternResolver 的实例自动装配到您的应用程序组件中,而不是 ResourceLoader

(#resources-as-dependencies)2.7. Resources as Dependencies

2.7. 资源作为依赖项

If the bean itself is going to determine and supply the resource path through some sort of dynamic process, it probably makes sense for the bean to use the ResourceLoader or ResourcePatternResolver interface to load resources. For example, consider the loading of a template of some sort, where the specific resource that is needed depends on the role of the user. If the resources are static, it makes sense to eliminate the use of the ResourceLoader interface (or ResourcePatternResolver interface) completely, have the bean expose the Resource properties it needs, and expect them to be injected into it.
如果豆本身将通过某种动态过程来确定和提供资源路径,那么豆使用 ResourceLoaderResourcePatternResolver 接口来加载资源可能是有意义的。例如,考虑加载某种模板,其中所需的特定资源取决于用户的角色。如果资源是静态的,那么完全消除使用 ResourceLoader 接口(或 ResourcePatternResolver 接口)是有意义的,让豆公开它需要的 Resource 属性,并期望它们被注入其中。

What makes it trivial to then inject these properties is that all application contexts register and use a special JavaBeans PropertyEditor, which can convert String paths to Resource objects. For example, the following MyBean class has a template property of type Resource.
使其能够轻松注入这些属性的原因在于,所有应用程序上下文都注册并使用一个特殊的 JavaBeans PropertyEditor ,它可以将 String 路径转换为 Resource 对象。例如,以下 MyBean 类有一个 template 属性,其类型为 Resource

package example; public class MyBean { private Resource template; public setTemplate(Resource template) { this.template = template; } // ... }
class MyBean(var template: Resource)

In an XML configuration file, the template property can be configured with a simple string for that resource, as the following example shows:
在一个 XML 配置文件中, template 属性可以用一个简单的字符串来配置该资源,如下例所示:

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

Note that the resource path has no prefix. Consequently, because the application context itself is going to be used as the ResourceLoader, the resource is loaded through a ClassPathResource, a FileSystemResource, or a ServletContextResource, depending on the exact type of the application context.
请注意,资源路径没有前缀。因此,由于应用程序上下文本身将被用作 ResourceLoader ,资源将通过一个 ClassPathResource ,一个 FileSystemResource ,或一个 ServletContextResource 来加载,具体取决于应用程序上下文的类型。

If you need to force a specific Resource type to be used, you can use a prefix. The following two examples show how to force a ClassPathResource and a UrlResource (the latter being used to access a file in the filesystem):
如果您需要强制使用特定的 Resource 类型,可以使用前缀。以下两个示例展示了如何强制使用 ClassPathResourceUrlResource (后者用于访问文件系统中的文件):

<property name="template" value="classpath:some/resource/path/myTemplate.txt">
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>

If the MyBean class is refactored for use with annotation-driven configuration, the path to myTemplate.txt can be stored under a key named template.path— for example, in a properties file made available to the Spring Environment ( see Environment Abstraction). The template path can then be referenced via the @Value annotation using a property placeholder (see Using @Value). Spring will retrieve the value of the template path as a string, and a special PropertyEditor will convert the string to a Resource object to be injected into the MyBean constructor. The following example demonstrates how to achieve this.
如果将 MyBean 类重构为使用注解驱动的配置,则可以将 myTemplate.txt 的路径存储在名为 template.path 的键下——例如,在提供给 Spring Environment 的属性文件中(见环境抽象)。然后可以通过使用属性占位符通过 @Value 注解引用模板路径(见使用 @Value )。Spring 将检索模板路径的值作为字符串,并使用特殊的 PropertyEditor 将字符串转换为要注入到 MyBean 构造函数的 Resource 对象。以下示例演示了如何实现这一点。

@Component public class MyBean { private final Resource template; public MyBean(@Value("${template.path}") Resource template) { this.template = template; } // ... }
@Component class MyBean(@Value("\${template.path}") private val template: Resource)

If we want to support multiple templates discovered under the same path in multiple locations in the classpath — for example, in multiple jars in the classpath — we can use the special classpath*: prefix and wildcarding to define a templates.path key as classpath*:/config/templates/*.txt. If we redefine the MyBean class as follows, Spring will convert the template path pattern into an array of Resource objects that can be injected into the MyBean constructor.
如果我们想在类路径的多个位置支持同一路径下发现的多个模板——例如,在类路径中的多个 jar 文件中——我们可以使用特殊的 classpath*: 前缀和通配符来定义一个 templates.path 键作为 classpath*:/config/templates/*.txt 。如果我们按如下方式重新定义 MyBean 类,Spring 将把模板路径模式转换为可以注入到 MyBean 构造函数的 Resource 对象数组。

@Component public class MyBean { private final Resource templates; public MyBean(@Value("${templates.path}") Resource templates) { this.templates = templates; } // ... }
@Component class MyBean(@Value("\${templates.path}") private val templates: Resource)

2.8. 应用上下文和资源路径

This section covers how to create application contexts with resources, including shortcuts that work with XML, how to use wildcards, and other details.
本节涵盖如何创建带有资源的应用程序上下文,包括与 XML 一起工作的快捷方式、如何使用通配符以及其他细节。

(#resources-app-ctx-construction)2.8.1. Constructing Application Contexts

2.8.1. 构建应用程序上下文

An application context constructor (for a specific application context type) generally takes a string or array of strings as the location paths of the resources, such as XML files that make up the definition of the context.
一个应用程序上下文构造函数(针对特定应用程序上下文类型)通常接受字符串或字符串数组作为资源的位置路径,例如构成上下文定义的 XML 文件。

When such a location path does not have a prefix, the specific Resource type built from that path and used to load the bean definitions depends on and is appropriate to the specific application context. For example, consider the following example, which creates a ClassPathXmlApplicationContext:
当这样的位置路径没有前缀时,从该路径构建的特定 Resource 类型以及用于加载 bean 定义的类型取决于并适用于特定的应用程序上下文。例如,考虑以下示例,它创建了一个 ClassPathXmlApplicationContext :

ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
val ctx = ClassPathXmlApplicationContext("conf/appContext.xml")

The bean definitions are loaded from the classpath, because a ClassPathResource is used. However, consider the following example, which creates a FileSystemXmlApplicationContext:
类路径中加载了 Bean 定义,因为使用了 ClassPathResource 。然而,考虑以下示例,它创建了一个 FileSystemXmlApplicationContext

ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");
val ctx = FileSystemXmlApplicationContext("conf/appContext.xml")

Now the bean definitions are loaded from a filesystem location (in this case, relative to the current working directory).
现在,Bean 定义是从文件系统位置加载的(在这种情况下,相对于当前工作目录)。

Note that the use of the special classpath prefix or a standard URL prefix on the location path overrides the default type of Resource created to load the bean definitions. Consider the following example:
请注意,在位置路径上使用特殊的 classpath 前缀或标准 URL 前缀将覆盖为加载 bean 定义而创建的默认 Resource 类型。考虑以下示例:

ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
val ctx = FileSystemXmlApplicationContext("classpath:conf/appContext.xml")

Using FileSystemXmlApplicationContext loads the bean definitions from the classpath. However, it is still a FileSystemXmlApplicationContext. If it is subsequently used as a ResourceLoader, any unprefixed paths are still treated as filesystem paths.
使用 FileSystemXmlApplicationContext 从类路径加载 bean 定义。然而,它仍然是一个 FileSystemXmlApplicationContext 。如果随后用作一个 ResourceLoader ,则任何未加前缀的路径仍然被视为文件系统路径。

(#resources-app-ctx-classpathxml)ConstructingClassPathXmlApplicationContext Instances — Shortcuts

构建 ClassPathXmlApplicationContext 实例 — 快捷方式

The ClassPathXmlApplicationContext exposes a number of constructors to enable convenient instantiation. The basic idea is that you can supply merely a string array that contains only the filenames of the XML files themselves (without the leading path information) and also supply a Class. The ClassPathXmlApplicationContext then derives the path information from the supplied class.
ClassPathXmlApplicationContext 公开了多个构造函数,以实现方便的实例化。基本思想是,您可以仅提供一个包含仅包含 XML 文件自身文件名的字符串数组(不包含路径信息),并且也提供 Class 。然后 ClassPathXmlApplicationContext 从提供的类中推导出路径信息。

Consider the following directory layout:
考虑以下目录结构:

com/ example/ services.xml repositories.xml MessengerService.class

The following example shows how a ClassPathXmlApplicationContext instance composed of the beans defined in files named services.xml and repositories.xml (which are on the classpath) can be instantiated:
以下示例展示了如何实例化一个由名为 services.xmlrepositories.xml (位于类路径上)的文件中定义的 bean 组成的 ClassPathXmlApplicationContext 实例:

ApplicationContext ctx = new ClassPathXmlApplicationContext( new String {"services.xml", "repositories.xml"}, MessengerService.class);
val ctx = ClassPathXmlApplicationContext(arrayOf("services.xml", "repositories.xml"), MessengerService::class.java)

See the ClassPathXmlApplicationContext javadoc for details on the various constructors.
查看 ClassPathXmlApplicationContext javadoc 以获取有关各种构造函数的详细信息。

(#resources-app-ctx-wildcards-in-resource-paths)2.8.2. Wildcards in Application Context Constructor Resource Paths

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

The resource paths in application context constructor values may be simple paths (as shown earlier), each of which has a one-to-one mapping to a target Resource or, alternately, may contain the special classpath*: prefix or internal Ant-style patterns (matched by using Spring’s PathMatcher utility). Both of the latter are effectively wildcards.
应用程序上下文构造函数中的资源路径可能是简单路径(如前所述),每个路径都与一个目标 Resource 有一对一映射,或者可以包含特殊的 classpath*: 前缀或内部 Ant 风格模式(使用 Spring 的 PathMatcher 实用工具进行匹配)。后两者都是有效的通配符。

One use for this mechanism is when you need to do component-style application assembly. All components can publish context definition fragments to a well-known location path, and, when the final application context is created using the same path prefixed with classpath*:, all component fragments are automatically picked up.
这种机制的一个用途是在需要以组件样式进行应用程序组装时。所有组件都可以将上下文定义片段发布到已知的路径位置,并且当使用相同路径前缀为 classpath*: 创建最终应用程序上下文时,所有组件片段将自动被选中。

Note that this wildcarding is specific to the use of resource paths in application context constructors (or when you use the PathMatcher utility class hierarchy directly) and is resolved at construction time. It has nothing to do with the Resource type itself. You cannot use the classpath*: prefix to construct an actual Resource, as a resource points to just one resource at a time.
请注意,这种通配符匹配仅适用于应用程序上下文构造函数中资源路径的使用(或直接使用 PathMatcher 工具类层次结构时),并在构造时解析。这与 Resource 类型本身无关。您不能使用 classpath*: 前缀来实际构造一个 Resource ,因为资源一次只能指向一个资源。

(#resources-app-ctx-ant-patterns-in-paths)Ant-style Patterns 蚂蚁风格模式

Path locations can contain Ant-style patterns, as the following example shows:
路径位置可以包含 Ant 风格的模式,如下例所示:

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

When the path location contains an Ant-style pattern, the resolver follows a more complex procedure to try to resolve the wildcard. It produces a Resource for the path up to the last non-wildcard segment and obtains a URL from it. If this URL is not a jar: URL or container-specific variant (such as zip: in WebLogic, wsjar in WebSphere, and so on), a java.io.File is obtained from it and used to resolve the wildcard by traversing the filesystem. In the case of a jar URL, the resolver either gets a java.net.JarURLConnection from it or manually parses the jar URL and then traverses the contents of the jar file to resolve the wildcards.
当路径位置包含 Ant 风格的模式时,解析器会遵循更复杂的程序来尝试解析通配符。它为路径直到最后一个非通配符段生成一个 Resource ,并从中获取一个 URL。如果这个 URL 不是一个 jar: URL 或容器特定的变体(例如 WebLogic 中的 zip: ,WebSphere 中的 wsjar 等),则从中获取一个 java.io.File 并用于通过遍历文件系统来解析通配符。在 jar URL 的情况下,解析器要么从其中获取一个 java.net.JarURLConnection ,要么手动解析 jar URL,然后遍历 jar 文件的内容来解析通配符。

(#resources-app-ctx-portability)Implications on Portability

对可移植性的影响

If the specified path is already a file URL (either implicitly because the base ResourceLoader is a filesystem one or explicitly), wildcarding is guaranteed to work in a completely portable fashion.
如果指定的路径已经是 file URL(无论是由于基础 ResourceLoader 是文件系统而隐式地,还是显式地),通配符匹配将保证以完全可移植的方式工作。

If the specified path is a classpath location, the resolver must obtain the last non-wildcard path segment URL by making a Classloader.getResource() call. Since this is just a node of the path (not the file at the end), it is actually undefined (in the ClassLoader javadoc) exactly what sort of a URL is returned in this case. In practice, it is always a java.io.File representing the directory (where the classpath resource resolves to a filesystem location) or a jar URL of some sort (where the classpath resource resolves to a jar location). Still, there is a portability concern on this operation.
如果指定的路径是 classpath 位置,解析器必须通过执行 Classloader.getResource() 调用来获取最后一个非通配符路径段 URL。由于这只是一个路径节点(而不是文件末尾的文件),在 ClassLoader javadoc 中实际上并没有明确说明在这种情况下返回的 URL 类型。在实践中,它始终是一个表示目录的 java.io.File (其中类路径资源解析到文件系统位置)或某种类型的 jar URL(其中类路径资源解析到 jar 位置)。尽管如此,这个操作仍然存在可移植性问题。

If a jar URL is obtained for the last non-wildcard segment, the resolver must be able to get a java.net.JarURLConnection from it or manually parse the jar URL, to be able to walk the contents of the jar and resolve the wildcard.
如果获得了最后一个非通配符段的 jar URL,解析器必须能够从中获取 java.net.JarURLConnection ,或者手动解析 jar URL,以便遍历 jar 的内容并解析通配符。
This does work in most environments but fails in others, and we strongly recommend that the wildcard resolution of resources coming from jars be thoroughly tested in your specific environment before you rely on it.
这在大多数环境中都有效,但在其他环境中会失败,我们强烈建议在您特定的环境中在依赖它之前,彻底测试来自 jar 的资源通配符解析。

(#resources-classpath-wildcards)Theclasspath*: Prefixclasspath*: 前缀

When constructing an XML-based application context, a location string may use the special classpath*: prefix, as the following example shows:
在构建基于 XML 的应用程序上下文时,位置字符串可能使用特殊的 classpath*: 前缀,如下例所示:

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

This special prefix specifies that all classpath resources that match the given name must be obtained (internally, this essentially happens through a call to ClassLoader.getResources(…​)) and then merged to form the final application context definition.
这个特殊前缀指定了必须获取所有与给定名称匹配的类路径资源(在内部,这本质上是通过调用 ClassLoader.getResources(…​) 来实现的),然后合并以形成最终的应用程序上下文定义。

The wildcard classpath relies on the getResources() method of the underlying ClassLoader. As most application servers nowadays supply their own ClassLoader implementation, the behavior might differ, especially when dealing with jar files. A simple test to check if classpath* works is to use the ClassLoader to load a file from within a jar on the classpath: getClass().getClassLoader().getResources("<someFileInsideTheJar>"). Try this test with files that have the same name but reside in two different locations — for example, files with the same name and same path but in different jars on the classpath.
通配符类路径依赖于底层 ClassLoadergetResources() 方法。由于大多数应用服务器现在都提供自己的 ClassLoader 实现,因此行为可能会有所不同,尤其是在处理 jar 文件时。检查 classpath* 是否正常工作的一种简单方法是使用 ClassLoader 从类路径中的 jar 文件中加载一个文件: getClass().getClassLoader().getResources("<someFileInsideTheJar>") 。尝试使用位于两个不同位置的具有相同名称的文件进行此测试——例如,具有相同名称和路径但位于类路径中不同 jar 中的文件。
In case an inappropriate result is returned, check the application server documentation for settings that might affect the ClassLoader behavior.
如果返回了不适当的结果,请检查应用程序服务器文档中可能影响 ClassLoader 行为设置的配置。

You can also combine the classpath*: prefix with a PathMatcher pattern in the rest of the location path (for example, classpath*:META-INF/*-beans.xml). In this case, the resolution strategy is fairly simple: A ClassLoader.getResources() call is used on the last non-wildcard path segment to get all the matching resources in the class loader hierarchy and then, off each resource, the same PathMatcher resolution strategy described earlier is used for the wildcard subpath.
您还可以将 classpath*: 前缀与位置路径其余部分的 PathMatcher 模式结合(例如, classpath*:META-INF/*-beans.xml )。在这种情况下,解析策略相当简单:在最后一个非通配符路径段上使用 ClassLoader.getResources() 调用,以获取类加载器层次结构中所有匹配的资源,然后,对于每个资源,使用之前描述的相同的 PathMatcher 解析策略来解析通配符子路径。

(#resources-wildcards-in-path-other-stuff)Other Notes Relating to Wildcards

其他有关通配符的注意事项

Note that classpath*:, when combined with Ant-style patterns, only works reliably with at least one root directory before the pattern starts, unless the actual target files reside in the file system. This means that a pattern such as classpath*:*.xml might not retrieve files from the root of jar files but rather only from the root of expanded directories.
请注意,当与 Ant 风格模式结合使用时, classpath*: 仅在模式开始前至少有一个根目录的情况下才能可靠地工作,除非实际目标文件位于文件系统中。这意味着像 classpath*:*.xml 这样的模式可能无法从 jar 文件的根目录检索文件,而只能从展开的目录的根目录检索。

Spring’s ability to retrieve classpath entries originates from the JDK’s ClassLoader.getResources() method, which only returns file system locations for an empty string (indicating potential roots to search). Spring evaluates URLClassLoader runtime configuration and the java.class.path manifest in jar files as well, but this is not guaranteed to lead to portable behavior.
Spring 检索类路径条目的能力源自 JDK 的 ClassLoader.getResources() 方法,该方法仅对空字符串(表示潜在的搜索根)返回文件系统位置。Spring 还会评估 jar 文件中的 URLClassLoader 运行时配置和 java.class.path 清单,但这并不保证导致可移植行为。

The scanning of classpath packages requires the presence of corresponding directory entries in the classpath. When you build JARs with Ant, do not activate the files-only switch of the JAR task. Also, classpath directories may not get exposed based on security policies in some environments — for example, stand-alone applications on JDK 1.7.0_45 and higher (which requires 'Trusted-Library' to be set up in your manifests. See https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources).
类路径包的扫描需要类路径中存在相应的目录条目。当您使用 Ant 构建 JAR 文件时,不要激活 JAR 任务的 files-only 开关。此外,在某些环境的安全策略下,类路径目录可能不会暴露——例如,JDK 1.7.0_45 及更高版本的独立应用程序(需要在您的清单中设置'Trusted-Library' 。请参阅 https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources)。

On JDK 9’s module path (Jigsaw), Spring’s classpath scanning generally works as expected. Putting resources into a dedicated directory is highly recommendable here as well, avoiding the aforementioned portability problems with searching the jar file root level.
在 JDK 9 的模块路径(Jigsaw)上,Spring 的类路径扫描通常按预期工作。在此处也将资源放入专用目录也是高度推荐的,以避免上述与搜索 jar 文件根级别的可移植性问题。

Ant-style patterns with classpath: resources are not guaranteed to find matching resources if the root package to search is available in multiple classpath locations. Consider the following example of a resource location:
Ant 风格的模式使用 classpath: 资源时,如果要搜索的根包在多个 classpath 位置可用,则无法保证找到匹配的资源。考虑以下资源位置的示例:

com/mycompany/package1/service-context.xml

Now consider an Ant-style path that someone might use to try to find that file:
现在考虑某人可能会使用的蚂蚁风格路径来尝试找到那个文件:

classpath:com/mycompany/**/service-context.xml

Such a resource may exist in only one location in the classpath, 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 desired resource may not exist in the first location found. Therefore, in such cases you should prefer using classpath*: with the same Ant-style pattern, which searches all classpath locations that contain the com.mycompany base package: classpath*:com/mycompany/**/service-context.xml.
此类资源可能只存在于类路径中的一个位置,但当使用如前例所示的路径尝试解析时,解析器将根据 getResource("com/mycompany"); 返回的第一个 URL 进行操作。如果基本包节点存在于多个 ClassLoader 位置,则所需资源可能不在找到的第一个位置。因此,在这种情况下,您应优先使用与 Ant 样式模式相同的 classpath*: ,该模式搜索包含 com.mycompany 基本包的所有类路径位置: classpath*:com/mycompany/**/service-context.xml

(#resources-filesystemresource-caveats)2.8.3.FileSystemResource Caveats 2.8.3.FileSystemResource 注意事项

A FileSystemResource that is not attached to a FileSystemApplicationContext (that is, when a FileSystemApplicationContext is not the actual ResourceLoader) treats absolute and relative paths as you would expect. Relative paths are relative to the current working directory, while absolute paths are relative to the root of the filesystem.
一个未附加到 FileSystemApplicationContext (即,当 FileSystemApplicationContext 不是一个实际的 ResourceLoader 时)的 FileSystemResource 会像您预期的那样处理绝对路径和相对路径。相对路径相对于当前工作目录,而绝对路径相对于文件系统的根目录。

For backwards compatibility (historical) reasons however, this changes when the FileSystemApplicationContext is the ResourceLoader. The FileSystemApplicationContext forces all attached FileSystemResource instances to treat all location paths as relative, whether they start with a leading slash or not. In practice, this means the following examples are equivalent:
然而,出于向后兼容(历史)原因,当 FileSystemApplicationContextResourceLoader 时,这种变化发生。 FileSystemApplicationContext 强制所有附加的 FileSystemResource 实例将所有位置路径视为相对路径,无论它们是否以斜杠开头。在实践中,这意味着以下示例是等效的:

ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/context.xml");
val ctx = FileSystemXmlApplicationContext("conf/context.xml")
ApplicationContext ctx = new FileSystemXmlApplicationContext("/conf/context.xml");
val ctx = FileSystemXmlApplicationContext("/conf/context.xml")

The following examples are also equivalent (even though it would make sense for them to be different, as one case is relative and the other absolute):
以下示例也等效(尽管它们应该不同,因为一个是相对的,另一个是绝对的):

FileSystemXmlApplicationContext ctx = ...; ctx.getResource("some/resource/path/myTemplate.txt");
val ctx: FileSystemXmlApplicationContext = ... ctx.getResource("some/resource/path/myTemplate.txt")
FileSystemXmlApplicationContext ctx = ...; ctx.getResource("/some/resource/path/myTemplate.txt");
val ctx: FileSystemXmlApplicationContext = ... ctx.getResource("/some/resource/path/myTemplate.txt")

In practice, if you need true absolute filesystem paths, you should avoid using absolute paths with FileSystemResource or FileSystemXmlApplicationContext and force the use of a UrlResource by using the file: URL prefix. The following examples show how to do so:
实际上,如果您需要真正的绝对文件系统路径,应避免使用带有 FileSystemResourceFileSystemXmlApplicationContext 的绝对路径,并强制使用 UrlResource 通过使用 file: URL 前缀。以下示例展示了如何做到这一点:

// actual context type doesn't matter, the Resource will always be UrlResource ctx.getResource("file:///some/resource/path/myTemplate.txt");
// actual context type doesn't matter, the Resource will always be UrlResource ctx.getResource("file:///some/resource/path/myTemplate.txt")
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource ApplicationContext ctx = new FileSystemXmlApplicationContext("file:///conf/context.xml");
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource val ctx = FileSystemXmlApplicationContext("file:///conf/context.xml")
ON THIS PAGE