文章参考来源:Spring Framework官方文档
首先,Java的标准java.net.URL类和各种URL前缀的标准处理程序不足以满足对低级资源的所有访问。
例如,没有标准化的URL实现可用于访问需要从类路径或相对于ServletContext获得的资源。虽然可以为专业注册新处理程序URL前缀(类似于现有的前缀,如http处理程序:),这通常是非常复杂的,和URL接口仍然缺乏一些可取的功能,比如一个方法来检查存在的资源被指出。
1. 关于org.springframework.core.io.Resource 接口
Spring中Resource接口代码如下:
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 接口:
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
其中,几个Resource接口中较为重要的方法为:
- getInputStream():定位并打开资源,返回一个用于从资源读取的InputStream。期望每次调用返回一个新的InputStream。关闭流是调用者的责任。
- exists():返回一个布尔值,指示该资源是否以物理形式存在。
- isOpen():返回一个布尔值,指示该资源是否表示打开流的句柄。如果为true,则InputStream不能被读取多次,必须只读取一次,然后关闭,以避免资源泄漏。对于所有通常的资源实现返回false,但InputStreamResource例外。
- getDescription():返回该资源的描述,用于使用该资源时的错误输出。这通常是资源的完全限定文件名或实际URL。
- 其他方法允许您获取表示资源的实际URL或File对象(如果底层实现是兼容的并支持该功能的话)。
其中,几个Resource接口实现为:
- UrlResource
- ClassPathResource
- FileSystemResource
- PathResource
- ServletContextResource
- InputStreamResource
- ByteArrayResource
1.1 UrlResource
UrlResource包装了一个java.net.URL,可以用来访问任何通常可以通过URL访问的对象,比如文件、HTTPS目标、FTP目标等等。所有URL都有一个标准化的String表示,这样就可以使用适当的标准化前缀来表示来自另一URL类型的URL。这包括file:用于访问文件系统路径,https:用于通过https协议访问资源,ftp:用于通过ftp访问资源,以及其他。
UrlResource是通过Java代码显式地使用UrlResource构造函数创建的,但通常是在调用带有表示路径的String参数的API方法时隐式创建的。对于后一种情况,PropertyEditor最终决定要创建哪种类型的资源。如果路径字符串包含一个众所周知的(到属性编辑器)前缀(例如classpath:),它将为该前缀创建一个适当的专门化资源。但是,如果它不能识别前缀,它就假定字符串是标准URL字符串,并创建一个UrlResource。
1.2 ClassPathResource
ClassPathResource这个类表示应该从类路径获得的资源。它使用线程上下文类装入器、给定类装入器或给定类来装入资源。
如果类路径资源驻留在文件系统中,但是类路径资源驻留在jar中,并且没有被(通过servlet引擎或其他环境)扩展到文件系统,则此资源实现支持以java.io.File的形式进行解析。为了解决这个问题,各种资源实现总是支持以java.net.URL的方式进行解析。
ClassPathResource是通过Java代码显式地使用ClassPathResource构造函数创建的,但通常是在调用一个API方法时隐式创建的,该API方法接受一个表示路径的String参数。对于后一种情况,JavaBeans PropertyEditor会识别字符串路径上的特殊前缀类路径classpath:,并在这种情况下创建一个ClassPathResource。
1.3 FileSystemResource
FileSystemResource是为了处理java.io.File的一个Resource接口实现。它还支持java.nio.file.Path的处理,应用Spring标准的基于字符串的路径转换,但通过java.nio.file.Files API执行所有操作。对于基于纯java.nio.path的支持,请使用PathResource。FileSystemResource支持以文件和URL的形式解析。
1.4 PathResource
PathResource是为了处理java.nio.file.Path的一个Resource接口实现,通过Path API执行所有操作和转换。它支持以文件和URL的形式进行解析,还实现了扩展的WritableResource接口。PathResource实际上是一个纯java.nio.path的基于FileSystemResource的替代方案,具有不同的createrrelative行为。
1.5 ServletContextResource
ServletContextResource是一个用于ServletContext资源的Resource接口实现,用于解释相关web应用根目录中的相对路径。
它总是支持流访问和URL访问,但只有当web应用程序存档被扩展且资源物理上位于文件系统中时,才允许java.io.File访问。无论它是在文件系统上扩展还是直接从JAR或数据库(这是可以想象的)等其他地方访问,实际上都依赖于Servlet容器。
1.6 InputStreamResource
InputStreamResource是给定InputStream的资源实现。只有在没有特定的Resource实现适用时才应该使用它。特别是,如果可能的话,最好使用ByteArrayResource或任何基于文件的Resource实现。
与其他资源实现相比,这是一个已经打开的资源的描述符。因此,它从isOpen()返回true。如果你需要将资源描述符保存在某个地方,或者你需要多次读取流,不要使用它。
1.7 ByteArrayResource
ByteArrayResource是给定byte数组的资源实现。它为给定的byte数组创建一个ByteArrayInputStream。
它对于从任何给定的byte数组加载内容非常有用,而不必求助于一次性使用的InputStreamResource。
2. ResourceLoader接口
ResourceLoader接口是由能够返回(即加载)Resource实例的对象实现的。即ResourceLoader可以返回一个Resource。
public interface ResourceLoader {
Resource getResource(String location);
ClassLoader getClassLoader();
}
所有应用程序上下文(application contexts)都实现ResourceLoader接口。因此,可以使用所有应用程序上下文来获取Resource实例。
当您在特定的应用程序上下文中调用getResource(),而指定的位置路径没有特定的前缀时,您将获得适合于该特定应用程序上下文中的Resource类型。例如,假设下面的代码片段是针对ClassPathXmlApplicationContext实例运行的:
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
那么,对于ClassPathXmlApplicationContext,该代码返回一个ClassPathResource。如果对FileSystemXmlApplicationContext实例运行相同的方法,它将返回一个FileSystemResource。对于WebApplicationContext,它将返回一个ServletContextResource。类似地,它将为每个上下文返回适当的对象。
另一方面,也可以通过指定特殊的ClassPathResource:前缀强制使用ClassPathResource,而不管应用程序上下文类型是什么,如下例所示:
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
类似地,可以通过指定任何标准java.net.URL前缀来强制使用UrlResource。下面的示例使用file和https前缀:
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注意事项。 |
| https: | https://myserver/logo.png | 作为URL加载 |
| (none) | /data/config.xml | 取决于底层的ApplicationContext |
3. ResourcePatternResolver 接口
ResourcePatternResolver接口是ResourceLoader接口的扩展,它定义了将位置格式(例如, Ant-style模式)解析到Resource资源对象的策略。
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException;
}
任何标准ApplicationContext中的默认ResourceLoader实际上是实现了ResourcePatternResolver接口的PathMatchingResourcePatternResolver的一个实例。对于ApplicationContext实例本身也是如此,它也实现了ResourcePatternResolver接口并委托给默认的PathMatchingResourcePatternResolver。
4. ResourceLoaderAware 接口
ResourceLoaderAware接口是一个特殊的回调接口,它标识希望提供ResourceLoader引用的组件(components)。
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自动装配模式(如autotowiring collaborator中所述)能够分别为构造函数参数和setter方法参数提供ResourceLoader。要获得更多的灵活性(包括自动装配字段和多个参数方法的能力),请考虑使用基于注释的自动装配特性。在这种情况下,ResourceLoader将自动连接到期望使用ResourceLoader类型的字段、构造函数参数或方法参数中,只要该字段、构造函数或方法带有@Autowired注释。有关更多信息,请参见使用@Autowired。
5. ApplicationContext 和Resource路径
应用程序上下文构造函数(用于特定的应用程序上下文类型)通常采用字符串或字符串数组作为资源的位置路径,例如组成上下文定义的XML文件。
当这样的位置路径没有前缀时,从该路径构建并用于加载bean定义的特定Resource类型依赖于并适合于特定的应用程序上下文。例如,考虑以下示例,它创建了一个ClassPathXmlApplicationContext:
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
bean定义是从类路径加载的,因为使用了ClassPathResource。但是,考虑下面的示例,它创建了一个FileSystemXmlApplicationContext,从文件系统位置(在本例中,相对于当前工作目录)加载bean定义:
ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");
注意,在位置路径上使用特殊的类路径前缀或标准URL前缀会覆盖为加载bean定义而创建的默认Resource类型。考虑下面的例子:
ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
使用FileSystemXmlApplicationContext从类路径加载bean定义。然而,它仍然是一个FileSystemXmlApplicationContext。如果它随后被用作ResourceLoader,那么任何没有前缀的路径仍然被视为文件系统路径。
6. 通配符 classpath*:
classpath*:的使用示例:
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
这个特殊的前缀指定必须获得与给定名称匹配的所有类路径资源(在内部,这实际上是通过调用ClassLoader.getResources(…)),然后合并以形成最终的应用程序上下文定义。
通配符类路径依赖于底层ClassLoader的getResources()方法。由于目前大多数应用服务器都提供自己的ClassLoader实现,因此行为可能有所不同,特别是在处理jar文件时。检查classpath*是否有效的一个简单测试是使用ClassLoader从类路径上的一个jar中加载一个文件:getClass(). getclassloader (). getresources ("")。使用具有相同名称但位于两个不同位置的文件尝试此测试—例如,具有相同名称和相同路径但位于类路径上不同jar中的文件。如果返回的结果不合适,请检查应用服务器文档中可能影响ClassLoader行为的设置。
您还可以将classpath*:前缀与PathMatcher模式组合在位置路径的其余部分(例如,classpath:META-INF/*-beans.xml*)。在这种情况下,解决策略是相当简单的:一个ClassLoader.getResources()调用用于non-wildcard路径段最后一个类加载器层次结构中的所有匹配的资源,然后每个资源,前面描述的相同PathMatcher解决策略用于通配符子路径。
本文详细介绍了Spring框架中的Resource接口及其各种实现,包括UrlResource、ClassPathResource、FileSystemResource等,以及ResourceLoader和ResourcePatternResolver接口的使用。此外,还探讨了ApplicationContext与Resource路径以及classpath*:通配符的应用。
645

被折叠的 条评论
为什么被折叠?



