20200103 Spring官方文档(Core 2)
2. Resources
2.1。介绍
不幸的是,Java的标准java.net.URL
类和用于各种URL前缀的标准处理程序不足以满足所有对低级资源的访问。 例如,没有标准化的URL实现可用于访问需要从类路径或相对于ServletContext
获得的资源。 虽然可以注册用于特殊URL前缀的新处理程序(类似于用于诸如http:
的现有前缀的处理程序),但这通常相当复杂,并且URL接口仍然缺少某些理想的功能,例如用于检查指向的资源是否存在的方法。
2.2。Resource 接口
Spring的Resource
接口旨在成为一种功能更强大的接口,用于抽象化对低级资源的访问
Resource
接口中一些最重要的方法是:
getInputStream()
:找到并打开资源,并返回一个InputStream以供读取资源。预计每次调用都会返回一个新的 InputStream。调用者有责任关闭流。exists()
:返回boolean指示此资源是否实际以物理形式存在。isOpen()
:返回boolean指示此资源是否代表具有打开流的句柄。如果为true,则不能多次读取InputStream,必须只读取一次,然后将其关闭以避免资源泄漏。所有常用资源实现(InputStreamResource除外)返回false。getDescription()
:返回对此资源的描述,以便在使用该资源时用于错误输出。这通常是标准文件名或资源的实际URL。
当需要资源时,Spring本身广泛使用Resource抽象作为许多方法签名中的参数类型。 一些Spring API中的其他方法(例如,各种ApplicationContext实现的构造函数)采用String形式,该字符串以未经修饰或简单的形式用于创建适合于该上下文实现的Resource,或者通过String路径上的特殊前缀,让调用者指定 必须创建并使用特定的Resource实现。
尽管Spring经常使用Resource接口,但实际上,在您自己的代码中单独用作通用实用工具类来访问资源也非常有用,即使您的代码不了解或不关心其他任何东西。 虽然这将您的代码耦合到Spring,但实际上仅将其耦合到这套实用程序类,它们充当URL的更强大替代,并且可以被视为等同于您将用于此目的的任何其他库。
Resource抽象并没有替代URL的功能。它尽可能地包装它。例如,UrlResource包装URL,然后使用包装的URL完成其工作。
2.3。内置Resource实现
2.3.1。 UrlResource
UrlResource
包装一个java.net.URL
,可用于访问通常可以通过URL访问的任何对象,例如文件,HTTP目标,FTP目标等。所有URL都有一个标准化的String表示形式,因此,使用适当的标准化前缀来表示另一种URL类型。这包括file:
访问文件系统路径,http:
通过HTTP协议访问资源,ftp:
通过FTP访问资源等。
UrlResource
是由Java代码通过显式使用UrlResource构造函数创建的,但通常在调用带有String参数表示路径的API方法时隐式创建。 对于后一种情况,JavaBeans PropertyEditor
最终决定要创建哪种类型的资源。 如果路径字符串包含众所周知的前缀(例如,classpath:
),则它将为该前缀创建适当的专用资源。 但是,如果它不能识别前缀,则假定该字符串是标准URL字符串并创建一个UrlResource。
2.3.2。 ClassPathResource
此类表示应从类路径获取的资源。它使用线程上下文类加载器,给定的类加载器或给定的类来加载资源。
如果类路径资源驻留在文件系统中,而不是驻留在jar中并且尚未通过servlet引擎或任何环境将其扩展到文件系统的类路径资源驻留在文件系统中,则此Resource实现以java.io.File
支持解析。为了解决这个问题,各种Resource实现始终支持以java.net.URL
解析。
ClassPathResource
是由Java代码通过显式使用ClassPathResource 构造函数创建的,但通常在调用带有用于表示路径的String参数的API方法时隐式创建 。对于后一种情况,JavaBeans PropertyEditor会在字符串路径中识别特殊前缀classpath:
,并在这种情况下创建一个ClassPathResource。
2.3.3。 FileSystemResource
这是java.io.File
和java.nio.file.Path
句柄的Resource实现。 它支持解析为File和URL。
2.3.4。 ServletContextResource
这是ServletContext资源的Resource实现,它解释相关Web应用程序根目录中的相对路径。
它始终支持流访问和URL访问,但仅在扩展Web应用程序档案且资源实际位于文件系统上时才允许java.io.File
访问。 它是在文件系统上扩展还是直接扩展,或者直接从JAR或其他类似数据库(可以想到的)中访问,实际上取决于Servlet容器。
2.3.5。 InputStreamResource
InputStreamResource
是给定InputStream的Resource实现。 仅当没有特定的资源实现适用时才应使用它。 特别是,在可能的情况下,最好选择ByteArrayResource或任何基于文件的Resource实现。
与其他Resource实现相反,这是一个已经打开的资源的描述符。 因此,它的isOpen()
返回true。 如果需要将资源描述符保留在某个地方,或者需要多次读取流,请不要使用它。
2.3.6。 ByteArrayResource
这是给定字节数组的Resource实现。 它为给定的字节数组创建一个ByteArrayInputStream
。
这对于从任何给定的字节数组加载内容很有用,而不必求助于一次性InputStreamResource
。
2.4。ResourceLoader
ResourceLoader
接口旨在由可以返回(即加载)Resource实例的对象实现。
当您在特定的应用程序上下文中调用getResource()
,并且指定的位置路径没有特定的前缀时,您将获得适合该特定应用程序上下文的Resource类型。 例如,假设针对ClassPathXmlApplicationContext实例执行了以下代码片段:
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
针对ClassPathXmlApplicationContext
,该代码返回ClassPathResource
。如果对FileSystemXmlApplicationContext
实例执行相同的方法,则将返回 FileSystemResource
。对于WebApplicationContext
,它将返回 ServletContextResource
。类似地,它将为每个上下文返回适当的对象。
另一方面,无论应用程序上下文类型如何,您都可以通过指定特殊前缀classpath:
来强制使用ClassPathResource
,如以下示例所示:
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
同样,您可以通过指定任何标准 java.net.URL
前缀来强制使用UrlResource
。
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
下表总结了将String对象转换为Resource对象的策略:
前缀 | 示例 | 说明
---|---
classpath: | classpath:com/myapp/config.xml | 从类路径加载。
file: | file:///data/config.xml | 从文件系统作为URL加载。另请参见FileSystemResource警告。
http: | https://myserver/logo.png | 载入为URL。
(none) | /data/config.xml | 取决于底层ApplicationContext实现。
2.5。ResourceLoaderAware接口
ResourceLoaderAware
接口是一个特殊的回调接口,用于标识期望提供ResourceLoader
引用的组件。
当一个类实现ResourceLoaderAware
并部署到应用程序上下文中(作为Spring托管bean)时,该类被应用程序上下文识别为ResourceLoaderAware
。然后应用程序上下文调用setResourceLoader(ResourceLoader)
,将自身作为参数提供(请记住,Spring中的所有应用程序上下文都实现了ResourceLoader
接口)。
由于ApplicationContext
是ResourceLoader
,因此bean也可以实现 ApplicationContextAware
接口并直接使用提供的应用程序上下文来加载资源。但是,通常,如果需要ResourceLoader 的话,最好使用专用接口。该代码将仅耦合到资源加载接口(可以视为实用程序接口),而不耦合到整个Spring ApplicationContext接口。
在应用程序组件中,您还可以依靠自动装配ResourceLoader
来实现ResourceLoaderAware接口。 “传统”构造函数和byType自动装配模式能够分别为构造函数参数或setter方法参数提供ResourceLoader。 为了获得更大的灵活性(包括自动装配字段和多个参数方法的能力),请考虑使用基于注释的自动装配功能。 在这种情况下,只要有问题的字段,构造函数或方法带有@Autowired批注,ResourceLoader就会自动装载到需要ResourceLoader类型的字段,构造函数参数或方法参数中。
2.6。Resource 作为依赖项
如果myBean具有type为Resource的template属性,则可以为该Resource配置一个简单的字符串
<bean id="myBean" class="...">
<property name="template" value="some/resource/path/myTemplate.txt"/>
</bean>
如果需要强制使用特定Resource类型,则可以使用前缀。以下两个示例显示了如何强制使用ClassPathResource和UrlResource(后者用于访问文件系统文件):
<property name="template" value="classpath:some/resource/path/myTemplate.txt">
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>
2.7。应用程序上下文和资源路径
2.7.1。构造应用程序上下文
应用程序上下文构造函数(针对特定的应用程序上下文类型)通常采用字符串或字符串数组作为资源的位置路径,例如构成上下文定义的XML文件。
当这样的位置路径没有前缀时,从该路径构建并用于加载Bean定义的特定Resource类型取决于特定的应用程序上下文,并且适用于该特定的应用程序上下文。例如,请考虑以下示例,该示例创建一个 ClassPathXmlApplicationContext:
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
2.7.2。应用程序上下文构造函数资源路径中的通配符
应用程序上下文构造函数值中的资源路径可以是简单路径,每个路径都具有到目标资源的一对一映射,或者可以包含特殊的 classpath*:
前缀或内部Ant样式正则表达式(使用Spring的PathMatcher实用程序进行匹配)。后者都是有效的通配符。
这种机制的一种用途是当您需要进行组件样式的应用程序组装时。所有组件都可以将上下文定义片段“发布”到一个众所周知的位置路径,并且,当使用前缀为 classpath*:
的相同路径创建最终应用程序上下文时,所有组件片段都会被自动提取。
请注意,此通配符特定于在应用程序上下文构造函数中使用资源路径(或当您直接使用PathMatcher
实用程序类层次结构时),并且在构造时已解决。它与Resource类型本身无关。您无法使用 classpath*:
前缀构造一个实际的Resource,因为资源一次仅指向一个资源。
Ant风格的模式
/WEB-INF/-context.xml
com/mycompany//applicationContext.xml
file:C:/some/path/-context.xml
classpath:com/mycompany//applicationContext.xml
当路径位置包含Ant样式的模式时,解析程序将遵循更复杂的过程来尝试解析通配符。 它为到达最后一个非通配符段的路径生成Resource,并从中获取URL。 如果此URL不是jar:URL
或特定于容器的变体(例如WebLogic中的zip:,WebSphere中的wsjar等),则从中获取java.io.File
并将其用于遍历文件系统。 对于jar URL,解析器可以从中获取java.net.JarURLConnection
,也可以手动解析jar URL,然后遍历jar文件的内容以解析通配符。
classpath*:前缀
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
通配符类路径依赖于基础类加载器的getResources()
方法。由于当今大多数应用程序服务器都提供自己的类加载器实现,因此行为可能有所不同,尤其是在处理jar文件时。
2.7.3。FileSystemResource注意事项
未附加到FileSystemApplicationContext
的FileSystemResource
(即,当FileSystemApplicationContext不是实际的ResourceLoader时)将按您期望的方式处理绝对路径和相对路径。 相对路径是相对于当前工作目录的,而绝对路径是相对于文件系统的根的。
但是,出于向后兼容性(历史)的原因,当FileSystemApplicationContext
是ResourceLoader
时,此情况会更改。 FileSystemApplicationContext
强制所有附加的FileSystemResource
实例将所有位置路径都视为相对位置,无论它们是否以前斜杠开头。 实际上,这意味着以下示例是等效的:
ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/context.xml");
ApplicationContext ctx = new FileSystemXmlApplicationContext("/conf/context.xml");
实际上,如果您需要真实的绝对文件系统路径,则应避免将绝对路径与FileSystemResource
或FileSystemXmlApplicationContext
一起使用,而应通过使用file:URL
前缀来强制使用UrlResource
。以下示例显示了如何执行此操作:
ctx.getResource("file:///some/resource/path/myTemplate.txt");
ApplicationContext ctx = new FileSystemXmlApplicationContext("file:///conf/context.xml");