Spring学习笔记11_资源(Resources)

本文详细介绍了Spring框架中的Resource接口及其各种实现,包括UrlResource、ClassPathResource、FileSystemResource等,以及ResourceLoader和ResourcePatternResolver接口的使用。此外,还探讨了ApplicationContext与Resource路径以及classpath*:通配符的应用。

文章参考来源: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接口实现为:

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解决策略用于通配符子路径。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值