跳到主要内容
版本:7.0.3

资源

Hunyuan 7b 中英对照 Resources

本章介绍了Spring如何处理资源,以及如何在Spring中操作资源。它包括以下主题:

介绍

Java的标准java.net.URL类以及针对各种URL前缀的标准处理程序,遗憾的是,并不足以满足所有对低级资源访问的需求。例如,目前没有标准化的URL实现可以用来访问需要从类路径(classpath)中获取的资源,或者相对于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,必须只读取一次然后关闭它以避免资源泄漏。除了 InputStreamResource 之外,所有常见的资源实现都返回 false

  • getDescription(): 返回对该资源的描述,用于在处理资源时输出错误信息。这通常是资源的完全限定文件名或实际 URL。

其他方法允许你获取一个实际的 URLFile 对象来表示该资源(如果底层实现是兼容的并且支持该功能的话)。

某些Resource接口的实现还实现了扩展的WritableResource接口,这种资源支持对其写入操作。

Spring 本身广泛使用 Resource 抽象,当需要资源时,它在许多方法签名中作为参数类型出现。在某些 Spring API 中的其他方法(例如各种 ApplicationContext 实现的构造函数)会接受一个 String 类型,这个 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 类型。例如,file: 用于访问文件系统路径,https: 用于通过 HTTPS 协议访问资源,ftp: 用于通过 FTP 访问资源,等等。

UrlResource 是通过 Java 代码显式使用 UrlResource 构造器来创建的,但当您调用一个接受表示路径的 String 参数的 API 方法时,它通常会以隐式方式被创建。在后一种情况下,JavaBeans 的 PropertyEditor 会最终决定要创建哪种类型的 Resource。如果路径字符串包含一个为 PropertyEditor 所识别的前缀(例如 classpath:),它就会为该前缀创建一个相应的专门 Resource。然而,如果 PropertyEditor 无法识别该前缀,它会假设该字符串是一个标准的 URL 字符串,并创建一个 UrlResource

ClassPathResource

这个类表示一个应该从类路径(classpath)中获取的资源。它使用线程上下文类加载器(thread context class loader)、给定的类加载器(given class loader),或者给定的类(given class)来加载资源。

这种Resource实现支持将类路径资源解析为java.io.File对象,前提是该资源位于文件系统中;但对于那些位于jar文件中、且尚未被(无论是通过servlet引擎还是其他方式)解压到文件系统中的类路径资源,则不支持这种解析方式。为了解决这个问题,各种Resource实现始终支持将此类资源解析为java.net.URL对象。

ClassPathResource可以通过Java代码显式地使用ClassPathResource构造函数来创建,但通常在调用一个接受代表路径的String参数的API方法时也会隐式地创建。在后一种情况下,JavaBeans的PropertyEditor会识别字符串路径中的特殊前缀classpath:,并在这种情况下创建一个ClassPathResource

FileSystemResource

这是一个用于java.io.File句柄的Resource实现。它也支持java.nio.file.Path句柄,应用Spring标准的基于字符串的路径转换,但所有操作都是通过java.nio.file.Files API来执行的。如果需要纯粹基于java.nio.path.Path的支持,请使用PathResource代替。FileSystemResource既支持解析为File,也支持解析为URL

PathResource

这是一个针对java.nio.file.Path处理器的Resource实现,它通过Path API执行所有操作和转换。它支持解析为FileURL,同时也实现了扩展的WritableResource接口。PathResource实际上是基于纯java.nio.path.PathFileSystemResource的替代品,其createRelative行为有所不同。

ServletContextResource

这是一个用于ServletContext资源的Resource实现,它能够解析相关Web应用程序根目录内的相对路径。

它始终支持流访问和URL访问,但只有当Web应用程序归档文件被解压,并且资源实际存在于文件系统中时,才允许通过java.io.File进行访问。是否进行解压以及资源是否存在于文件系统中,或者是否直接从JAR文件或其他地方(例如数据库)进行访问(这也是可能的),实际上取决于Servlet容器。

InputStreamResource

InputStreamResource是针对给定InputStreamResource实现。只有在没有合适的特定Resource实现时才应使用它。特别是,在可能的情况下,应优先选择ByteArrayResource或任何基于文件的Resource实现。

与其他Resource实现不同,这是一个用于已经打开的资源描述符。因此,isOpen()方法会返回true。如果你需要将资源描述符保存在某个地方,或者需要多次读取流数据,那么不要使用这个实现。

ByteArrayResource

这是一个针对给定字节数组的Resource实现。它为该字节数组创建一个ByteArrayInputStream

它有助于从任何给定的字节数组中加载内容,而无需使用一次性的InputStreamResource

ResourceLoader 接口

ResourceLoader接口旨在由能够返回(即加载)Resource实例的对象来实现。以下列表展示了ResourceLoader接口的定义:

public interface ResourceLoader {

Resource getResource(String location);

ClassLoader getClassLoader();
}

所有应用程序上下文都实现了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。以下示例使用了 filehttps 前缀:

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

下表总结了将String对象转换为Resource对象的策略:

表1. 资源字符串

前缀示例说明
classpath:classpath:com/myapp/config.xml从类路径(classpath)加载。
file:file:///data/config.xml作为 URL 从文件系统加载。另请参阅 FileSystemResource 注意事项
https:https://myserver/logo.png作为 URL 加载。
(none)/data/config.xml取决于底层的 ApplicationContext。(即不指定加载方式时使用的默认路径)

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 语义提供的那个)也可以检查它是否也实现了这个扩展接口。

PathMatchingResourcePatternResolver 是一个独立的实现,可以在 ApplicationContext 之外使用,同时也被 ResourceArrayPropertyEditor 用来填充 Resource[] 类型的 bean 属性。PathMatchingResourcePatternResolver 能够将指定的资源路径解析为一个或多个匹配的 Resource 对象。源路径可以是一个简单的路径,它与目标 Resource 有一一对应的关系;或者,该路径也可以包含特殊的 classpath*: 前缀和/或内部 Ant 风格的正则表达式(这些正则表达式的匹配是通过 Spring 的 org.springframework.util.AntPathMatcher 工具来完成的)。后两种情况实际上都属于通配符的运用。

备注

实际上,任何标准ApplicationContext中的默认ResourceLoader都是PathMatchingResourcePatternResolver的实例,而PathMatchingResourcePatternResolver实现了ResourcePatternResolver接口。对于ApplicationContext实例本身也是如此,它同样实现了ResourcePatternResolver接口,并将任务委托给默认的PathMatchingResourcePatternResolver

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接口的替代方案。传统的constructorbyType自动注入模式(如Autowiring Collaborators中所描述)能够分别为构造函数参数或setter方法参数提供ResourceLoader。为了获得更大的灵活性(包括能够自动注入字段和多参数方法),可以考虑使用基于注解的自动注入功能。在这种情况下,只要相应的字段、构造函数或方法带有@Autowired注解,ResourceLoader就会自动注入到该字段、构造函数参数或方法参数中。更多信息,请参阅Using @Autowired

备注

对于包含通配符或使用特殊资源前缀 classpath*: 的资源路径,如果要加载一个或多个 Resource 对象,可以考虑在应用程序组件中自动注入 ResourcePatternResolver 的实例,而不是使用 ResourceLoader

资源作为依赖项

如果bean本身需要通过某种动态过程来确定和提供资源路径,那么让bean使用ResourceLoaderResourcePatternResolver接口来加载资源可能是合理的。例如,在加载某种模板时,所需的具体资源可能会根据用户的角色而有所不同。如果资源是静态的,那么完全可以不使用ResourceLoader接口(或ResourcePatternResolver接口),让bean暴露它所需的Resource属性,并期望这些属性被注入到bean中。

使得之后注入这些属性变得简单的是,所有的应用程序上下文都会注册并使用一个特殊的JavaBeans PropertyEditor,该编辑器能够将String路径转换为Resource对象。例如,以下MyBean类有一个类型为Resourcetemplate属性。

public class MyBean {

private Resource template;

public setTemplate(Resource template) {
this.template = template;
}

// ...
}

在XML配置文件中,template属性可以用一个简单的字符串来为该资源进行配置,如下例所示:

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

请注意,资源路径没有前缀。因此,由于应用程序上下文本身将被用作ResourceLoader,所以资源的加载方式取决于应用程序上下文的精确类型,可以通过ClassPathResourceFileSystemResourceServletContextResource来进行加载。

如果你需要强制使用特定的Resource类型,可以使用前缀。以下两个示例展示了如何强制使用ClassPathResourceUrlResource(后者用于访问文件系统中的文件):

<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的键下——例如,在一个可供SpringEnvironment使用的属性文件中(参见环境抽象)。然后可以通过@Value注解引用模板路径,并使用属性占位符(参见使用@Value)。Spring会将模板路径的值作为字符串获取,而一个特殊的PropertyEditor会将这个字符串转换为一个Resource对象,以便注入到MyBean的构造函数中。以下示例演示了如何实现这一点。

@Component
public class MyBean {

private final Resource template;

public MyBean(@Value("${template.path}") Resource template) {
this.template = template;
}

// ...
}

如果我们要支持在类路径(classpath)的多个位置下的同一路径下发现的多个模板——例如,在类路径中的多个jar文件中——我们可以使用特殊的classpath*:前缀和通配符来定义templates.path键,将其设置为classpath*:/config/templates/*.txt。如果我们按照以下方式重新定义MyBean类,Spring会将模板路径模式转换成一个Resource对象的数组,这些对象可以被注入到MyBean的构造函数中。

@Component
public class MyBean {

private final Resource[] templates;

public MyBean(@Value("${templates.path}") Resource[] templates) {
this.templates = templates;
}

// ...
}

应用上下文和资源路径

本节介绍如何使用资源创建应用程序上下文,包括与XML配合使用的快捷方式、如何使用通配符以及其他相关细节。

构建应用程序上下文

应用程序上下文构造器(针对特定类型的应用程序上下文)通常接受一个字符串或字符串数组作为资源的路径,这些资源可能是构成该上下文定义的XML文件。

当这样的位置路径没有前缀时,从该路径构建的特定Resource类型以及用于加载bean定义的类型将取决于具体的应用程序上下文,并且需要与该上下文相适应。例如,考虑以下示例,该示例创建了一个ClassPathXmlApplicationContext

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

bean定义是从类路径(classpath)中加载的,因为这里使用了ClassPathResource。然而,考虑以下示例,该示例创建了一个FileSystemXmlApplicationContext

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

现在,bean定义是从文件系统位置加载的(在这种情况下,是相对于当前工作目录的)。

请注意,在路径上使用特殊的 classpath 前缀或标准的 URL 前缀会覆盖用于加载 bean 定义的默认 Resource 类型。考虑以下示例:

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

使用FileSystemXmlApplicationContext可以从类路径加载bean定义。然而,它仍然是一个FileSystemXmlApplicationContext。如果之后将其用作ResourceLoader,任何没有前缀的路径仍然会被视为文件系统路径。

构建ClassPathXmlApplicationContext实例——快捷方式

ClassPathXmlApplicationContext 提供了多个构造函数以便于实例化。其基本思路是,你只需提供一个仅包含 XML 文件名称的字符串数组(不包含路径信息),同时还可以提供一个 Class 对象。ClassPathXmlApplicationContext 会从提供的类中推导出路径信息。

考虑以下目录结构:

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

以下示例展示了如何实例化一个ClassPathXmlApplicationContext,该容器由在services.xmlrepositories.xml文件中定义的Bean组成(这些文件位于类路径上):

ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "repositories.xml"}, MessengerService.class);

有关各种构造函数的详细信息,请参阅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等),则会从该URL中获取一个java.io.File对象,并通过遍历文件系统来解析通配符。对于jar URL,解析器要么从中获取一个java.net.JarURLConnection对象,要么手动解析jar URL,然后遍历jar文件的内容来解析通配符。

对可移植性的影响

如果指定的路径已经是一个“文件”URL(要么是因为基础的ResourceLoader本身就是基于文件系统的,要么是明确指定的),那么使用通配符的方式肯定能够以完全可移植的方式正常工作。

如果指定的路径是classpath位置,那么解析器必须通过调用Classloader.getResource()来获取最后一个非通配符路径片段URL。由于这仅仅是路径中的一个节点(而不是路径末尾的文件),因此在ClassLoader的javadoc中实际上并没有明确说明在这种情况下返回的是什么类型的URL。在实践中,返回的总是表示目录的java.io.File(即类路径资源解析到的文件系统位置),或者是某种形式的jar URL(即类路径资源解析到的jar文件位置)。尽管如此,这种操作仍存在可移植性问题。

如果为最后一个非通配符段获取到了一个jar URL,那么解析器必须能够从该URL中获取到java.net.JarURLConnection,或者手动解析该jar URL,以便能够遍历jar文件的内容并解析通配符。这在大多数环境中是可以正常工作的,但在其他环境中则会失败。我们强烈建议,在依赖这种来自jar文件的资源的通配符解析功能之前,务必在您特定的环境中对其进行彻底的测试。

classpath*: 前缀

在构建基于XML的应用上下文时,位置字符串可以使用特殊的classpath*:前缀,如下例所示:

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

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

备注

通配类路径依赖于底层ClassLoadergetResources()方法。由于如今大多数应用服务器都提供自己实现的ClassLoader,因此其行为可能会有所不同,尤其是在处理jar文件时。一个简单的测试方法是使用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元数据,但这种方式并不能保证实现可移植的行为。

备注

扫描类路径(classpath)中的包需要类路径中存在相应的目录条目。使用Ant构建JAR文件时,不要启用JAR任务的files-only选项。此外,在某些环境中,由于安全策略的限制,类路径目录可能无法被访问——例如,在JDK 1.7.0_45及更高版本上的独立应用程序中(这种情况下需要在manifest文件中设置“Trusted-Library”选项)。请参阅stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources

在模块路径(Java Module System)中,Spring的类路径扫描通常能够按预期工作。在这种情况下,也强烈建议将资源放在专门的目录中,以避免上述在jar文件根目录进行搜索时遇到的可移植性问题。

如果要搜索的根包存在于多个类路径(classpath)位置中,那么使用classpath:资源的Ant风格模式并不能保证能找到匹配的资源。请考虑以下资源位置的示例:

com/mycompany/package1/service-context.xml

现在考虑一种类似Ant风格的路径,有人可能会使用这种路径来尝试找到那个文件:

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

这样的资源在类路径(classpath)中可能只存在一个位置,但当使用像前面例子中的路径来尝试解析它时,解析器会依据getResource("com/mycompany");返回的(第一个)URL来进行查找。如果这个基础包节点存在于多个ClassLoader的路径中,那么所需的资源可能并不在找到的第一个位置。因此,在这种情况下,你更应该使用带有相同Ant风格的模式classpath*:,它会搜索所有包含com.mycompany基础包的类路径位置:classpath*:com/mycompany/**/service-context.xml

FileSystemResource 注意事项

一个未连接到FileSystemApplicationContextFileSystemResource(即,当FileSystemApplicationContext不是实际的ResourceLoader时),会按照你的预期来处理绝对路径和相对路径。相对路径是相对于当前工作目录的,而绝对路径则是相对于文件系统的根目录的。

然而,出于向后兼容(历史原因),当FileSystemApplicationContext作为ResourceLoader时,情况就有所不同了。FileSystemApplicationContext强制所有关联的FileSystemResource实例将所有路径视为相对路径,无论这些路径是否以斜杠开头。实际上,这意味着以下示例是等价的:

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

以下示例也是等价的(尽管从逻辑上来说它们应该有所不同,因为一个是相对的,另一个是绝对的):

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

在实践中,如果你需要真正的绝对文件系统路径,你应该避免使用FileSystemResourceFileSystemXmlApplicationContext来获取绝对路径,而应该通过使用file:这样的URL前缀来强制使用UrlResource。以下示例展示了如何做到这一点:

// 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");