资源

简介 (Introduction)

Java 的标准java.net.URL类和各种 URL 前缀的标准处理程序,不幸的是,对于访问底层资源来说并不完全足够。例如,没有标准化的URL实现可以用来访问需要从类路径或相对于ServletContext获取的资源。虽然可以为专门的URL前缀注册新的处理程序(类似于现有处理程序,例如http:),但这通常非常复杂,并且URL接口仍然缺少一些理想的功能,例如检查所指向资源是否存在的方法。

Resource 接口 (The Resource Interface)

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不能多次读取,必须只读取一次,然后关闭以避免资源泄漏。对于所有常用的资源实现,返回falseInputStreamResource除外。

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

其他方法允许您获得表示资源的实际URLFile对象(如果底层实现兼容并支持该功能)。

一些Resource接口的实现还实现了扩展的WritableResource接口,用于支持写入的资源。

Spring 本身广泛使用Resource抽象,作为许多方法签名中的参数类型,当需要资源时。一些 Spring API 中的其他方法(例如各种ApplicationContext实现的构造函数)采用一个String,在未修饰或简单形式下,用于创建适合该上下文实现的Resource,或者通过String路径上的特殊前缀,让调用者指定必须创建和使用特定的Resource实现。

虽然Resource接口在 Spring 中被大量使用,并且由 Spring 使用,但它实际上非常方便作为实用程序类本身在您自己的代码中使用,用于访问资源,即使您的代码不知道或不关心 Spring 的任何其他部分。虽然这将您的代码与 Spring 耦合在一起,但它实际上只将其与这少量实用程序类耦合在一起,这可以作为URL的更强大的替代品,并且可以被认为等同于您为此目的使用的任何其他库。

Resource抽象不会替换功能。它在可能的情况下对其进行包装。例如,UrlResource包装一个 URL 并使用包装的URL来完成其工作。

内置Resource实现 (Built-in Resource Implementations)

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

这是一个用于 java.io.File句柄的 Resource 实现。它也支持 java.nio.file.Path 句柄,应用 Spring 的标准基于字符串的路径转换,但通过 java.nio.file.Files API 执行所有操作。对于纯基于 java.nio.path.Path 的支持,请改用 PathResourceFileSystemResource 支持解析为 FileURL

PathResource

这是一个用于 java.nio.file.Path 句柄的 Resource 实现,通过 Path API 执行所有操作和转换。它支持解析为 FileURL,并且还实现了扩展的 WritableResource 接口。PathResource 实际上是 FileSystemResource 的纯基于 java.nio.path.Path 的替代方案,具有不同的 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(),并且指定的 location path 没有特定前缀时,您将获得适合该特定应用程序上下文的 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。以下示例使用 filehttps 前缀

  • 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 对象的策略

表 1. 资源字符串
前缀 示例 说明

classpath

classpath:com/myapp/config.xml

从类路径加载。

file

file:///data/config.xml

从文件系统以 URL 加载。另请参见 FileSystemResource 注意事项

https

https://myserver/logo.png

URL 加载。

(无)

/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 语义提供的 ResourceLoader)是否也实现了此扩展接口。

PathMatchingResourcePatternResolver 是一个独立的实现,可在 ApplicationContext 之外使用,也由 ResourceArrayPropertyEditor 用于填充 Resource[] bean 属性。PathMatchingResourcePatternResolver 能够将指定的资源位置路径解析为一个或多个匹配的 Resource 对象。源路径可以是与目标 Resource 一一映射的简单路径,或者可以包含特殊的 classpath*: 前缀和/或内部 Ant 风格的正则表达式(使用 Spring 的 org.springframework.util.AntPathMatcher 实用程序匹配)。后两者实际上都是通配符。

任何标准 ApplicationContext 中的默认 ResourceLoader 实际上是 PathMatchingResourcePatternResolver 的一个实例,它实现了 ResourcePatternResolver 接口。ApplicationContext 实例本身也实现了 ResourcePatternResolver 接口并委托给默认的 PathMatchingResourcePatternResolver,情况也是如此。

ResourceLoaderAware 接口

ResourceLoaderAware 接口是一个特殊的回调接口,用于标识期望提供 ResourceLoader 引用的组件。下面的清单显示了 ResourceLoaderAware 接口的定义

public interface ResourceLoaderAware {

	void setResourceLoader(ResourceLoader resourceLoader);
}

当一个类实现 ResourceLoaderAware 并部署到应用程序上下文(作为 Spring 管理的 bean)时,应用程序上下文会将其识别为 ResourceLoaderAware。然后,应用程序上下文调用 setResourceLoader(ResourceLoader),并提供自身作为参数(记住,Spring 中的所有应用程序上下文都实现了 ResourceLoader 接口)。

由于 ApplicationContextResourceLoader,因此 bean 也可以实现 ApplicationContextAware 接口并直接使用提供的应用程序上下文来加载资源。但是,一般而言,如果只需要资源加载接口,最好使用专门的 ResourceLoader 接口。代码将仅与资源加载接口(可以将其视为实用程序接口)耦合,而不是与整个 Spring ApplicationContext 接口耦合。

在应用程序组件中,您可以依赖于ResourceLoader的自动装配,作为实现ResourceLoaderAware接口的替代方案。传统构造器byType自动装配模式(如自动装配协作者中所述)能够分别为构造器参数或setter方法参数提供一个ResourceLoader。为了获得更大的灵活性(包括自动装配字段和多个参数方法的能力),请考虑使用基于注解的自动装配功能。在这种情况下,只要相关的字段、构造器或方法带有@Autowired注解,ResourceLoader就会被自动装配到期望ResourceLoader类型的字段、构造器参数或方法参数中。更多信息,请参见使用@Autowired

要为包含通配符或使用特殊classpath*:资源前缀的资源路径加载一个或多个Resource对象,请考虑将ResourcePatternResolver的实例自动装配到您的应用程序组件中,而不是ResourceLoader

资源作为依赖项

如果bean本身将通过某种动态过程确定并提供资源路径,那么bean使用ResourceLoaderResourcePatternResolver接口加载资源可能更有意义。例如,考虑加载某种类型的模板,其中所需的特定资源取决于用户角色。如果资源是静态的,那么完全消除ResourceLoader接口(或ResourcePatternResolver接口)的使用,让bean公开其需要的Resource属性,并期望将它们注入到bean中是有意义的。

之所以能够轻松注入这些属性,是因为所有应用程序上下文都注册并使用一个特殊的JavaBeans PropertyEditor,它可以将String路径转换为Resource对象。例如,下面的MyBean类具有一个类型为Resourcetemplate属性。

  • 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,因此资源是通过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的键下——例如,在一个可用于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将把模板路径模式转换为一个Resource对象数组,这些对象可以注入到MyBean构造函数中。

  • 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

以下示例显示如何实例化一个由名为services.xmlrepositories.xml的文件(位于类路径上)中定义的bean组成的ClassPathXmlApplicationContext实例

  • 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(…​)实现的),然后合并以形成最终的应用程序上下文定义。

通配符类路径依赖于底层ClassLoadergetResources()方法。由于大多数应用程序服务器现在都提供自己的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 任务的files-only开关。此外,在某些环境中,由于安全策略,类路径目录可能无法公开——例如,JDK 1.7.0_45 及更高版本的独立应用程序(这需要在清单文件中设置“Trusted-Library”。参见stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources)。

在模块路径(Java 模块系统)上,Spring 的类路径扫描通常按预期工作。在这里,强烈建议将资源放入专用目录中,避免上述在搜索 jar 文件根级别时出现可移植性问题。

如果要搜索的根包在多个类路径位置可用,则使用classpath:资源的 Ant 风格模式不能保证找到匹配的资源。考虑以下资源位置示例

com/mycompany/package1/service-context.xml

现在考虑某人可能用来尝试查找该文件的 Ant 风格路径

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

此类资源可能仅存在于类路径中的一个位置,但是当使用前面示例中这样的路径尝试解析它时,解析器会根据getResource("com/mycompany");返回的(第一个)URL 进行操作。如果此基本包节点存在于多个ClassLoader位置,则所需资源可能不存在于找到的第一个位置。因此,在这种情况下,您应该更倾向于使用具有相同 Ant 风格模式的classpath*:,它会搜索包含com.mycompany基本包的所有类路径位置:classpath*:com/mycompany/**/service-context.xml

FileSystemResource注意事项

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

但是,出于向后兼容性(历史)原因,当FileSystemApplicationContextResourceLoader时,这种情况会发生变化。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")

实际上,如果您需要真正的绝对文件系统路径,则应避免对FileSystemResourceFileSystemXmlApplicationContext使用绝对路径,并通过使用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")