资源
本章介绍 Spring 如何处理资源以及如何在 Spring 中使用资源。它包括以下主题
介绍
Java 的标准 java.net.URL
类和各种 URL 前缀的标准处理程序,不幸的是,对于所有对底层资源的访问来说还不够充分。例如,没有标准化的 URL
实现可以用来访问需要从类路径或相对于 ServletContext
获取的资源。虽然可以为专门的 URL
前缀注册新的处理程序(类似于现有的 http:
等前缀的处理程序),但这通常非常复杂,而且 URL
接口仍然缺少一些理想的功能,例如检查所指向资源是否存在的方法。
Resource
接口
Spring 的 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();
}
正如 Resource
接口的定义所示,它扩展了 InputStreamSource
接口。以下列表显示了 InputStreamSource
接口的定义
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
Resource
接口中一些最重要的方法是
-
getInputStream()
:定位并打开资源,返回一个InputStream
用于从资源中读取。预计每次调用都会返回一个新的InputStream
。调用者有责任关闭流。 -
exists()
:返回一个boolean
值,指示此资源是否实际以物理形式存在。 -
isOpen()
:返回一个boolean
值,指示此资源是否表示具有打开流的句柄。如果为true
,则InputStream
不能多次读取,必须只读取一次,然后关闭,以避免资源泄漏。对于所有常见的资源实现,返回值为false
,InputStreamResource
除外。 -
getDescription()
:返回此资源的描述,用于在使用资源时进行错误输出。这通常是资源的完全限定文件名或实际 URL。
其他方法允许您获取表示资源的实际 URL
或 File
对象(如果底层实现兼容并支持该功能)。
Resource
接口的一些实现也实现了扩展的 WritableResource
接口,用于支持写入资源。
Spring 本身广泛使用 Resource
抽象,作为许多方法签名中的参数类型,当需要资源时。一些 Spring API 中的其他方法(例如各种 ApplicationContext
实现的构造函数)接受一个 String
,该字符串在未修饰或简单形式下用于创建适合该上下文实现的 Resource
,或者通过 String
路径上的特殊前缀,让调用者指定必须创建和使用的特定 Resource
实现。
虽然 Resource
接口在 Spring 中被广泛使用,但实际上它本身作为通用实用程序类非常方便,可以在您自己的代码中使用,用于访问资源,即使您的代码不知道或不关心 Spring 的任何其他部分。虽然这会将您的代码与 Spring 耦合,但它实际上只将您的代码耦合到这组小的实用程序类,它可以作为 URL
的更强大的替代品,并且可以被认为等同于您为此目的使用的任何其他库。
Resource 抽象不会替换功能。它尽可能地包装它。例如,UrlResource 包装一个 URL,并使用包装的 URL 来完成它的工作。
|
内置 Resource
实现
Spring 包含几个内置的 Resource
实现
有关 Spring 中可用的 Resource
实现的完整列表,请参阅 Resource
javadoc 的“所有已知实现类”部分。
UrlResource
UrlResource
包装一个 java.net.URL
,可用于访问通常可以使用 URL 访问的任何对象,例如文件、HTTPS 目标、FTP 目标等。所有 URL 都有一个标准化的 String
表示,以便使用适当的标准化前缀来区分一种 URL 类型与另一种 URL 类型。这包括用于访问文件系统路径的 file:
、用于通过 HTTPS 协议访问资源的 https:
、用于通过 FTP 访问资源的 ftp:
等等。
UrlResource
是通过 Java 代码显式使用 UrlResource
构造函数创建的,但当您调用接受 String
参数(表示路径)的 API 方法时,它通常是隐式创建的。对于后一种情况,JavaBeans PropertyEditor
最终决定要创建哪种类型的 Resource
。如果路径字符串包含一个已知(对属性编辑器而言)的前缀(例如 classpath:
),它将为该前缀创建一个适当的专用 Resource
。但是,如果它无法识别该前缀,它将假定该字符串是标准 URL 字符串,并创建一个 UrlResource
。
ClassPathResource
此类表示应从类路径获取的资源。它使用线程上下文类加载器、给定的类加载器或给定的类来加载资源。
此Resource
实现支持作为java.io.File
解析,如果类路径资源位于文件系统中,但不适用于位于 jar 中且未扩展到文件系统的类路径资源(由 servlet 引擎或任何环境)。为了解决这个问题,各种Resource
实现始终支持作为java.net.URL
解析。
ClassPathResource
由 Java 代码通过显式使用ClassPathResource
构造函数创建,但当您调用采用String
参数的 API 方法时,通常会隐式创建,该参数表示路径。对于后一种情况,JavaBeans PropertyEditor
识别字符串路径上的特殊前缀classpath:
,并在这种情况下创建ClassPathResource
。
FileSystemResource
这是一个Resource
实现,用于java.io.File
句柄。它还支持java.nio.file.Path
句柄,应用 Spring 的标准基于字符串的路径转换,但通过java.nio.file.Files
API 执行所有操作。对于纯java.nio.path.Path
基于支持,请使用PathResource
。FileSystemResource
支持作为File
和URL
解析。
PathResource
这是一个Resource
实现,用于java.nio.file.Path
句柄,通过Path
API 执行所有操作和转换。它支持作为File
和URL
解析,并且还实现了扩展的WritableResource
接口。PathResource
实际上是FileSystemResource
的纯java.nio.path.Path
基于的替代方案,具有不同的createRelative
行为。
ServletContextResource
这是一个Resource
实现,用于ServletContext
资源,它解释相关 Web 应用程序根目录中的相对路径。
它始终支持流访问和 URL 访问,但仅当 Web 应用程序存档已扩展且资源物理位于文件系统上时才允许java.io.File
访问。它是否已扩展并位于文件系统上,还是直接从 JAR 或其他地方(如数据库)访问(这是可以想象的)实际上取决于 Servlet 容器。
ResourceLoader
接口
ResourceLoader
接口旨在由可以返回(即加载)Resource
实例的对象实现。以下清单显示了 ResourceLoader
接口定义
public interface ResourceLoader {
Resource getResource(String location);
ClassLoader getClassLoader();
}
所有应用程序上下文都实现了 ResourceLoader
接口。因此,所有应用程序上下文都可以用来获取 Resource
实例。
当您在特定应用程序上下文中调用 getResource()
时,如果指定的路径没有特定前缀,您将获得适合该特定应用程序上下文的 Resource
类型。例如,假设以下代码片段针对 ClassPathXmlApplicationContext
实例运行
-
Java
-
Kotlin
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
val template = ctx.getResource("some/resource/path/myTemplate.txt")
针对 ClassPathXmlApplicationContext
,该代码返回一个 ClassPathResource
。如果在 FileSystemXmlApplicationContext
实例上运行相同的方法,它将返回一个 FileSystemResource
。对于 WebApplicationContext
,它将返回一个 ServletContextResource
。它将类似地为每个上下文返回适当的对象。
因此,您可以以适合特定应用程序上下文的方式加载资源。
另一方面,您也可以强制使用 ClassPathResource
,无论应用程序上下文类型如何,方法是指定特殊的前缀 classpath:
,如下例所示
-
Java
-
Kotlin
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
val template = ctx.getResource("classpath:some/resource/path/myTemplate.txt")
类似地,您可以通过指定任何标准的 java.net.URL
前缀来强制使用 UrlResource
。以下示例使用 file
和 https
前缀
-
Java
-
Kotlin
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
val template = ctx.getResource("file:///some/resource/path/myTemplate.txt")
-
Java
-
Kotlin
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
val template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt")
下表总结了将 String
对象转换为 Resource
对象的策略
前缀 | 示例 | 解释 |
---|---|---|
classpath |
|
从类路径加载。 |
file |
|
从文件系统中以 |
https |
|
以 |
(无) |
|
取决于底层的 |
ResourcePatternResolver
接口
ResourcePatternResolver
接口是ResourceLoader
接口的扩展,它定义了一种将位置模式(例如,Ant 风格的路径模式)解析为Resource
对象的策略。
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException;
}
如上所示,此接口还为类路径中的所有匹配资源定义了一个特殊的classpath*:
资源前缀。请注意,在这种情况下,资源位置应为不带占位符的路径,例如classpath*:/config/beans.xml
。JAR 文件或类路径中的不同目录可以包含具有相同路径和相同名称的多个文件。有关使用classpath*:
资源前缀的通配符支持的更多详细信息,请参见应用程序上下文构造函数资源路径中的通配符及其子节。
可以检查传入的ResourceLoader
(例如,通过ResourceLoaderAware
语义提供的ResourceLoader
)是否也实现了此扩展接口。
PathMatchingResourcePatternResolver
是一个独立的实现,可以在ApplicationContext
之外使用,也由ResourceArrayPropertyEditor
用于填充Resource[]
bean 属性。PathMatchingResourcePatternResolver
能够将指定的资源位置路径解析为一个或多个匹配的Resource
对象。源路径可以是与目标Resource
一一映射的简单路径,或者可以包含特殊的classpath*:
前缀和/或内部 Ant 风格的正则表达式(使用 Spring 的org.springframework.util.AntPathMatcher
实用程序匹配)。后两者实际上都是通配符。
任何标准 |
ResourceLoaderAware
接口
ResourceLoaderAware
接口是一个特殊的回调接口,它标识期望提供ResourceLoader
引用 的组件。以下清单显示了ResourceLoaderAware
接口的定义
public interface ResourceLoaderAware {
void setResourceLoader(ResourceLoader resourceLoader);
}
当一个类实现 ResourceLoaderAware
并部署到应用程序上下文(作为 Spring 管理的 Bean)中时,应用程序上下文会将其识别为 ResourceLoaderAware
。然后,应用程序上下文会调用 setResourceLoader(ResourceLoader)
,并将自身作为参数提供(请记住,Spring 中的所有应用程序上下文都实现了 ResourceLoader
接口)。
由于 ApplicationContext
是一个 ResourceLoader
,因此 Bean 也可以实现 ApplicationContextAware
接口,并直接使用提供的应用程序上下文来加载资源。但是,通常情况下,如果只需要加载资源,最好使用专门的 ResourceLoader
接口。代码将只与资源加载接口(可以认为是实用程序接口)耦合,而不是与整个 Spring ApplicationContext
接口耦合。
在应用程序组件中,您也可以依赖于 ResourceLoader
的自动装配,作为实现 ResourceLoaderAware
接口的替代方案。传统 的 构造函数
和 byType
自动装配模式(如 自动装配协作者 中所述)能够分别为构造函数参数或 setter 方法参数提供 ResourceLoader
。为了获得更大的灵活性(包括自动装配字段和多个参数方法的能力),请考虑使用基于注解的自动装配功能。在这种情况下,只要字段、构造函数或方法带有 @Autowired
注解,ResourceLoader
就会自动装配到期望 ResourceLoader
类型的字段、构造函数参数或方法参数中。有关更多信息,请参阅 使用 @Autowired
。
要为包含通配符或使用特殊 classpath*: 资源前缀的资源路径加载一个或多个 Resource 对象,请考虑将 ResourcePatternResolver 的实例自动装配到您的应用程序组件中,而不是 ResourceLoader 。
|
资源作为依赖项
如果 Bean 本身要通过某种动态过程来确定和提供资源路径,那么它可能需要使用 ResourceLoader
或 ResourcePatternResolver
接口来加载资源。例如,考虑加载某种模板,其中所需的特定资源取决于用户的角色。如果资源是静态的,那么可以完全消除使用 ResourceLoader
接口(或 ResourcePatternResolver
接口),让 Bean 公开它需要的 Resource
属性,并期望它们被注入到其中。
使注入这些属性变得非常简单的原因是,所有应用程序上下文都注册并使用一个特殊的 JavaBeans PropertyEditor
,它可以将 String
路径转换为 Resource
对象。例如,以下 MyBean
类具有一个类型为 Resource
的 template
属性。
-
Java
-
Kotlin
public class MyBean {
private Resource template;
public setTemplate(Resource template) {
this.template = template;
}
// ...
}
class MyBean(var template: Resource)
在 XML 配置文件中,template
属性可以使用简单的字符串来配置该资源,如下例所示
<bean id="myBean" class="example.MyBean">
<property name="template" value="some/resource/path/myTemplate.txt"/>
</bean>
请注意,资源路径没有前缀。因此,由于应用程序上下文本身将用作 ResourceLoader
,因此资源将通过 ClassPathResource
、FileSystemResource
或 ServletContextResource
加载,具体取决于应用程序上下文的具体类型。
如果需要强制使用特定 Resource
类型,可以使用前缀。以下两个示例展示了如何强制使用 ClassPathResource
和 UrlResource
(后者用于访问文件系统中的文件)
<property name="template" value="classpath:some/resource/path/myTemplate.txt">
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>
如果 MyBean
类被重构以用于基于注解的配置,则 myTemplate.txt
的路径可以存储在名为 template.path
的键下,例如,在提供给 Spring Environment
的属性文件中(参见 环境抽象)。然后,可以使用 @Value
注解通过属性占位符引用模板路径(参见 使用 @Value
)。Spring 将以字符串形式检索模板路径的值,一个特殊的 PropertyEditor
将把字符串转换为 Resource
对象,并将其注入到 MyBean
构造函数中。以下示例演示了如何实现这一点。
-
Java
-
Kotlin
@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)
如果我们希望支持在类路径中的多个位置(例如,类路径中的多个 jar 文件)下相同路径下发现的多个模板,我们可以使用特殊的 classpath*:
前缀和通配符来定义一个 templates.path
键为 classpath*:/config/templates/*.txt
。如果我们重新定义 MyBean
类如下,Spring 将把模板路径模式转换为可以注入到 MyBean
构造函数中的 Resource
对象数组。
-
Java
-
Kotlin
@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[])
应用程序上下文和资源路径
本节介绍如何创建包含资源的应用程序上下文,包括使用 XML 的快捷方式、如何使用通配符以及其他细节。
构建应用程序上下文
应用程序上下文构造函数(针对特定应用程序上下文类型)通常使用字符串或字符串数组作为资源的位置路径,例如构成上下文定义的 XML 文件。
当这样的位置路径没有前缀时,从该路径构建并用于加载 bean 定义的特定 Resource
类型取决于并适合于特定的应用程序上下文。例如,考虑以下示例,它创建了一个 ClassPathXmlApplicationContext
-
Java
-
Kotlin
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
val ctx = ClassPathXmlApplicationContext("conf/appContext.xml")
bean 定义从类路径加载,因为使用了 ClassPathResource
。但是,考虑以下示例,它创建了一个 FileSystemXmlApplicationContext
-
Java
-
Kotlin
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/appContext.xml");
val ctx = FileSystemXmlApplicationContext("conf/appContext.xml")
现在 bean 定义从文件系统位置加载(在本例中,相对于当前工作目录)。
请注意,在位置路径上使用特殊的 classpath
前缀或标准 URL 前缀会覆盖创建用于加载 bean 定义的 Resource
的默认类型。考虑以下示例
-
Java
-
Kotlin
ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
val ctx = FileSystemXmlApplicationContext("classpath:conf/appContext.xml")
使用 `FileSystemXmlApplicationContext` 从类路径加载 bean 定义。但是,它仍然是 `FileSystemXmlApplicationContext`。如果它随后用作 `ResourceLoader`,任何没有前缀的路径仍然被视为文件系统路径。
构造 `ClassPathXmlApplicationContext` 实例 - 快捷方式
`ClassPathXmlApplicationContext` 提供了许多构造函数,以方便实例化。基本思想是,您可以只提供一个包含 XML 文件名称的字符串数组(不包含前导路径信息),并提供一个 `Class`。`ClassPathXmlApplicationContext` 然后从提供的类中推导出路径信息。
考虑以下目录布局
com/ example/ services.xml repositories.xml MessengerService.class
以下示例展示了如何实例化一个 `ClassPathXmlApplicationContext` 实例,该实例由名为 `services.xml` 和 `repositories.xml` 的文件(位于类路径上)中定义的 bean 组成
-
Java
-
Kotlin
ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "repositories.xml"}, MessengerService.class);
val ctx = ClassPathXmlApplicationContext(arrayOf("services.xml", "repositories.xml"), MessengerService::class.java)
有关各种构造函数的详细信息,请参阅 `ClassPathXmlApplicationContext` javadoc。
应用程序上下文构造函数资源路径中的通配符
应用程序上下文构造函数值中的资源路径可以是简单路径(如前所示),每个路径都与目标 `Resource` 有一对一映射,或者可以包含特殊的 `classpath*:` 前缀或内部 Ant 风格的模式(使用 Spring 的 `PathMatcher` 实用程序匹配)。后两者实际上都是通配符。
此机制的一种用途是当您需要进行组件式应用程序组装时。所有组件都可以将上下文定义片段发布到一个众所周知的路径位置,并且当使用相同的路径(以 `classpath*:` 为前缀)创建最终的应用程序上下文时,所有组件片段都会被自动拾取。
请注意,此通配符功能特定于在应用程序上下文构造函数中使用资源路径(或者当您直接使用 `PathMatcher` 实用程序类层次结构时),并且在构造时解析。它与 `Resource` 类型本身无关。您不能使用 `classpath*:` 前缀来构造实际的 `Resource`,因为资源一次只能指向一个资源。
Ant 风格模式
路径位置可以包含 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 文件的内容来解析通配符。
对可移植性的影响
如果指定的路径已经是 file
URL(无论是隐式地因为基本 ResourceLoader
是文件系统,还是显式地),则通配符的使用将保证以完全可移植的方式工作。
如果指定的路径是 classpath
位置,则解析器必须通过调用 Classloader.getResource()
来获取最后一个非通配符路径段的 URL。由于这只是路径中的一个节点(而不是末尾的文件),因此在 ClassLoader
javadoc 中实际上没有定义在这种情况下返回的 URL 类型。在实践中,它始终是表示目录的 java.io.File
(其中类路径资源解析为文件系统位置)或某种类型的 jar URL(其中类路径资源解析为 jar 位置)。尽管如此,此操作仍然存在可移植性问题。
如果为最后一个非通配符段获取了 jar URL,则解析器必须能够从中获取一个 java.net.JarURLConnection
或手动解析 jar URL,以便能够遍历 jar 的内容并解析通配符。这在大多数环境中都能正常工作,但在其他环境中则会失败,我们强烈建议在依赖此功能之前,在您的特定环境中彻底测试来自 jar 的资源的通配符解析。
classpath*:
前缀
在构建基于 XML 的应用程序上下文时,位置字符串可以使用特殊的 classpath*:
前缀,如下例所示
-
Java
-
Kotlin
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
val ctx = ClassPathXmlApplicationContext("classpath*:conf/appContext.xml")
此特殊前缀指定必须获取与给定名称匹配的所有类路径资源(在内部,这实际上是通过调用ClassLoader.getResources(…)
来实现的),然后合并以形成最终的应用程序上下文定义。
通配符类路径依赖于底层ClassLoader 的getResources() 方法。由于大多数应用程序服务器现在都提供自己的ClassLoader 实现,因此行为可能会有所不同,尤其是在处理 jar 文件时。要检查classpath* 是否有效,一个简单的测试是使用ClassLoader 从类路径上的 jar 文件中加载文件:getClass().getClassLoader().getResources("<someFileInsideTheJar>") 。尝试使用具有相同名称但位于两个不同位置的文件进行此测试——例如,具有相同名称和相同路径但位于类路径上不同 jar 文件中的文件。如果返回不合适的结果,请查看应用程序服务器文档以了解可能影响ClassLoader 行为的设置。
|
您还可以将classpath*:
前缀与位置路径中的PathMatcher
模式结合使用(例如,classpath*:META-INF/*-beans.xml
)。在这种情况下,解析策略非常简单:在最后一个非通配符路径段上使用ClassLoader.getResources()
调用来获取类加载器层次结构中所有匹配的资源,然后,在每个资源上,使用前面描述的相同PathMatcher
解析策略来解析通配符子路径。
与通配符相关的其他说明
请注意,classpath*:
与 Ant 风格模式结合使用时,只有在模式开始之前至少有一个根目录的情况下才能可靠地工作,除非实际目标文件位于文件系统中。这意味着,诸如classpath*:*.xml
之类的模式可能无法从 jar 文件的根目录中检索文件,而只能从扩展目录的根目录中检索文件。
Spring 检索类路径条目的能力源于 JDK 的ClassLoader.getResources()
方法,该方法仅对空字符串(表示要搜索的潜在根目录)返回文件系统位置。Spring 还评估URLClassLoader
运行时配置和 jar 文件中的java.class.path
清单,但这不能保证可移植行为。
类路径包的扫描需要类路径中存在相应的目录条目。当您使用 Ant 构建 JAR 时,请勿激活 JAR 任务的 在 JDK 9 的模块路径(Jigsaw)上,Spring 的类路径扫描通常按预期工作。在这里,将资源放入专用目录中也是高度推荐的,可以避免前面提到的在搜索 jar 文件根级别时出现可移植性问题。 |
如果要搜索的根包在多个类路径位置可用,则使用classpath:
资源的 Ant 风格模式无法保证找到匹配的资源。请考虑以下资源位置示例
com/mycompany/package1/service-context.xml
现在考虑某人可能用来尝试查找该文件的 Ant 风格路径
classpath:com/mycompany/**/service-context.xml
这样的资源可能只存在于类路径中的一个位置,但是当使用类似于上述示例的路径来尝试解析它时,解析器会根据getResource("com/mycompany");
返回的(第一个)URL 进行操作。如果此基本包节点存在于多个ClassLoader
位置,则所需的资源可能不存在于找到的第一个位置。因此,在这种情况下,您应该优先使用classpath*:
以及相同的 Ant 风格模式,它将搜索包含com.mycompany
基本包的所有类路径位置:classpath*:com/mycompany/**/service-context.xml
。
FileSystemResource
注意事项
未附加到FileSystemApplicationContext
的FileSystemResource
(即,当FileSystemApplicationContext
不是实际的ResourceLoader
时)会按照您的预期处理绝对路径和相对路径。相对路径相对于当前工作目录,而绝对路径相对于文件系统的根目录。
但是,出于向后兼容性(历史)原因,当FileSystemApplicationContext
是ResourceLoader
时,情况会发生变化。FileSystemApplicationContext
强制所有附加的FileSystemResource
实例将所有位置路径视为相对路径,无论它们是否以斜杠开头。实际上,这意味着以下示例是等效的
-
Java
-
Kotlin
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/context.xml");
val ctx = FileSystemXmlApplicationContext("conf/context.xml")
-
Java
-
Kotlin
ApplicationContext ctx =
new FileSystemXmlApplicationContext("/conf/context.xml");
val ctx = FileSystemXmlApplicationContext("/conf/context.xml")
以下示例也是等效的(即使它们应该是不同的,因为一种情况是相对的,而另一种情况是绝对的)
-
Java
-
Kotlin
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt");
val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("some/resource/path/myTemplate.txt")
-
Java
-
Kotlin
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt");
val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("/some/resource/path/myTemplate.txt")
实际上,如果您需要真正的绝对文件系统路径,您应该避免在FileSystemResource
或FileSystemXmlApplicationContext
中使用绝对路径,并通过使用file:
URL 前缀强制使用UrlResource
。以下示例显示了如何执行此操作
-
Java
-
Kotlin
// 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")
-
Java
-
Kotlin
// 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")