Spring资源

1 简介

Java的标准java.net.URL类和各类URL前缀的标准处理器不足以知足全部的低级别资源访问。例如没有标准化的URL实现用于访问从类路径上获取的资源,或者与ServletContext相关的资源。尽管能够为特定的URL前缀注册新的处理器(与已有的前缀处理器例如http:类似),这通常是很复杂的,而且URL接口仍然缺少一些须要的方法,例如检查指向的资源是否存在的方法。java

2 Resource接口

Spring的Resource接口是一个更强大的接口,用于对低级资源的抽象访问。web

public interface Resource extends InputStreamSource {

    boolean exists();

    boolean isOpen();

    URL getURL() throws IOException;

    File getFile() throws IOException;

    Resource createRelative(String relativePath) throws IOException;

    String getFilename();

    String getDescription();

}
public interface InputStreamSource {

    InputStream getInputStream() throws IOException;

}

Resource接口中的一些最重要的方法包括:正则表达式

  • getInputStream():定位而且打开资源,返回用于读取资源的InputStream。指望每次调用都会返回一个新的InputStream。须要调用者关闭流。
  • exists():返回一个布尔值指示这个资源是否以物理形式存在;
  • isOpen():返回一个布尔值指示这个资源是否表示具备打开流的句柄。若是为true,InputStream不能被多重读取,而且必须被读取一次而后关闭来避免资源泄露。对于全部一般的资源实现,除了InputStreamResource以外,它是false。
  • getDescription():返回资源的描述,用于使用资源时的错误输出。它常常是全限定的文件名活着资源的实际URL。

其它方法容许你获取一个表明资源的实际URL或文件对象(若是底层实现兼容而且在功能上支持)。数组

Resource抽象在Spring自身被普遍的使用,在许多方法签名中当须要一个资源时它做为一个参数类型。在一些Spring API的其它方法中(例如各类ApplicationContext实现的构造方法),使用一个字符串,以未装饰或简单的形式用于建立适合该上下文实现的Resource,或者经过字符串路径中特定的前缀容许调用者指定必须建立和使用的特定Resource实现。服务器

在Resource接口被Spring普遍使用的同时,它实际上在用户编写的代码中做为通常的实用工具类也是很是有用的,主要用于访问资源,甚至当代码不知道或不关心Spring的任何其它部分时。尽管这将代码和Spring耦合在一块儿,它实际上仅仅耦合了一小部分工具类,做为URL的功能更丰富的替代,能够等价于出于这个目使用的其它任何库。app

要注意的是,资源抽象不是取代而是包装它。例如URLResource包装URL,而且使用被包装的URL完成它的工做。ide

3 内建的Resource实现

在Spring中提供了一些开箱即用的Resource实现。函数

3.1 UrlResource

UrlResource包装了java.net.URL,用于访问一般可经过URL访问的任何对象,例如文件、HTTP目标、FTP目标等。全部URL有一个标准化的字符串表示方法,适当的标准化前缀用于指示URL类型。file:表示访问文件系统路径,http:用于经过HTTP协议访问资源,ftp:用于经过FTP访问资源等等。工具

UrlResource对象在Java代码中使用构造函数显式建立,可是常常被隐式建立,经过调用使用一个String参数表示一个路径的API方法。对于后一种状况,一个JavaBean PropertyEditor最终会决定建立什么类型的Resource。若是路径字符串中包含一些众所周知的前缀例如classpath:,它会为该前缀建立一个恰当的资源。若是不能识别前缀,它会假定这仅仅是一个标准URL字符串,而且建立UrlResource。布局

3.2 ClassPathResource

这个类表明从类路径获取的资源。它或者使用线程上下文类加载器,一个给定的类加载器,或者使用一个用户给定的加载资源的类。

若是类路径资源位于文件系统,这个Resouce实现支持将其解析为java.io.File;可是不支持位于jar文件中且还没有解压(经过servlet引擎或其它环境)到文件系统中的类路径资源。为了解决这个问题各类资源实现老是支持解析为java.net.URL。

ClassPathResource在Java代码中使用ClassPathResource构造函数现实建立;可是常常被隐式建立,经过调用用一个String参数表示一个路径的API方法。对于后一种状况,一个JavaBean PropertyEditor会识别字符串路径的特定前缀classpath:,而后建立一个ClassPathResource。

3.3 FileSystemResource

用于处理java.io.File的Resource实现。它支持解析为一个File或URL。

3.4 ServletContextResource

这个Resource实现用于ServletContext资源,使用相应web应用程序的根目录来解析相关的路径。

它支持流读取和URL读取,可是仅仅当web应用压缩文件被解压而且资源存在于文件系统时容许访问java.io.File。压缩文件是否被解压到文件系统,仍是直接从JAR访问或者从其它地方例如DB访问,实际上依赖于Servlet容器。

3.5 InputStreamResource

用于给定的InputStream的Resource实现。只有在没有具体的资源实现适用的状况下才应该使用。实际上,应尽量优先使用ByteArrayResource或任何基于文件的Resource实现。与其它资源实现相反,这是一个对已经打开资源的描述符——所以isOpen()方法返回true。若是须要将资源描述符保存在某处或者须要屡次读取流,请不要使用它。

3.6 ByteArrayResource

这个Resource实现用于一个给定的byte数组。它为给定的数组建立一个ByteArrayInputStream。

从任何给定的byte数组读取内容而不需使用一次性的InputStreamResource是有用的.

4 ResourceLoader

ResourceLoader被可以返回(即加载)Resource实例的对象实现。

public interface ResourceLoader {

    Resource getResource(String location);

}

全部应用上下文都实现了ResourceLoader接口,所以全部应用上下文能够用于获取Resource实例。

当在特定的应用上下文上调用getResource(),而且给定的位置路径没有特定的前缀,它会返回与具体应用上下文相关的Resource类型。例如,假定下面的代码段有ClassPathXmlApplicationContext实例执行:

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

会返回一个ClassPathResource对象;若是一样的方法被FileSystemXmlAPplicationContext实例执行,会返回FileSystemResource。对于WebApplicationContext,则返回ServletContextResource等等。

所以能够以适合于特定应用程序上下文的方式加载资源。

另外一方面,也能够经过指定classpath:前缀来强制返回ClassPathResource,而不考虑应用上下文类型:

Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");

类似的,能够指定任何一个标准的java.net.URL前缀来强制返回UrlResource:

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

下面的表格总结了String转换为Resource的规则:

前缀 例子 解释
classpath: classpath:com/myapp/config.xml 从类路径加载
file: file:///data/config.xml 做为URL加载,从文件系统
http: http://myserver/logo.png 做为URL加
(没有前缀) /data/config.xml 依赖于底层的ApplicationContext

5 ResourceLoaderAware接口

ResourceLoaderAware接口是一个特殊的标记接口,识别但愿获取ResourceLoader引用的对象。

public interface ResourceLoaderAware {

    void setResourceLoader(ResourceLoader resourceLoader);
}

当一个类实现了ResourceLoaderAware接口而且被部署到应用上下文中(做为一个Spring管理的bean),它被应用上下文识别为ResourceLoaderAware。而后应用上下文将会调用setResourceLoader(ResourceLoader resourceLoader)方法,将自身做为参数提供给该方法(记住,Spring中全部的应用上下文都实现了ResourceLoader接口)。

固然,因为一个ApplicationContext是一个ResourceLoader,bean也能够实现ApplicationContextAware接口而且使用被提供的应用上下文直接用于加载资源,可是通常状况下,最好若是仅仅是须要加载资源使用特定的ResourceLoader接口。这样代码仅仅会与被认为是工具接口的资源加载接口耦合,而不是耦合于整个Spring ApplicationContext接口。

从Spring 2.5开始可使用ResourceLoader的自动装配做为实现ResourceLoaderAware接口的一种替代方法。传统的构造函数和经过类型的自动装配模式如今能够为构造函数参数和setter方法参数提供ResourceLoader依赖。对于更加灵活的方式(包括自动装配域和多个参数的方法),可使用新的基于注解的自动装配特征。在这种状况下,ResourceLoader能够被自动注入到期待类型为ResourceLoader的域、构造函数参数或者方法参数中,只有上述域、方法或者构造函数带有@Autowired注解。

6 资源做为依赖

若是一个bean自身要经过某种动态决定过程决定和提供资源路径,bean使用ResourceLoader接口加载资源是颇有意义的。做为一例子,考虑以某种模式加载一个模版,其中所需的特定资源取决于用户角色。若是资源是静态的,彻底消除对ResourceLoader的使用是有意义的,只要让bean暴露它须要的Resource属性,而且指望它们被注入到bean中。

全部应用上下文都会注册和做为一个特殊JavaBean使用的PropertyEdtior将String路径转换为Resource对象,它使得注入这些Resource依赖变的简单。因此若是myBean有一个Resource类型的模版属性,它能够被配置为一个简单的字符串,以下所示:

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

请注意资源路径没有前缀,因为应用上下文自身将会被用作ResourceLoader,资源自身被加载为ClassPathResource、FileSystemResource或者ServletContextResource取决于具体的上下文类型。

若是须要强制指定资源的类型,可使用前缀。下面的两个例子展现了如何强制使用ClassPathResource和UrlResource(后一种用于获取文件系统中的文件):

<property name="template" value="classpath:some/resource/path/myTemplate.txt">
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>

7 应用上下文和资源路径

7.1 构造应用上下文

一个应用上下文的构造函数(用于特定的应用上下文类型)通常使用一个字符串或字符串数组做为资源(例如XML文件)的位置路径,这些资源组成了上下文的定义。

当这样的位置路径没有前缀时,特定的Resource类型从这个路径构建而且用于加载bean定义,Resource类型依赖并适用于特定的应用上下文。例如,若是以下建立一个ClassPathXmlApplicationContext:

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

bean定义将会从类路径加载,由于使用了ClassPathResource。可是若是建立一个FileSystemXmlApplicationContext以下:

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

bean定义将会从文件系统位置加载,在这种状况下相对于当前工做目录。

请注意,在位置路径前使用特殊的类路径前缀或标准URL前缀将会覆盖默认的被建立用于加载bean定义的Resource类型。因此,这个FileSystemXmlApplicationContext

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

将会实际从类路径加载bean定义。然而,它仍然是一个FileSystemXmlApplicationContext。若是后来被做为ResourceLoader使用,任何无前缀的路径将仍然会做为文件系统路径被处理。

构造ClassPathXmlApplicationContext实例 —— 快捷方式

ClassPathXmlApplicationContext提供了系列构造函数用于快速实例化。基本的方法是替狗一个字符串数组包含了XML文件自身的文件名(没有路径信息),而且也能够提供一个Class;ClassPathXmlApplicationContext将会从被提供的类判断路径信息。

一个例子会使其清晰,考虑一个目录布局以下所示:

com/
	foo/
		services.xml
		daos.xml
		MessengerService.class

一个ClassPathXmlApplicationContext实例能够像下面这样被实例化,它是由在services.xml和daos.xml中的bean定义组成。

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

能够参考ClassPathXmlApplicationContext的javadoc获取各类构造函数的详细信息。

7.2 应用上下文构造函数的资源路径中的通配符

在应用上下文构造函数的参数值中的资源路径能够是简单路径(如上所示),与目标资源是一一映射的关系,或者能够包含特殊的"classpath*:"前缀和内部的Ant风格的正则表达式(使用Spring的PathMatcher工具匹配)。后者都是有效的通配符。

这种机制的一个用途是组件样式应用组装。全部组件能够发布上下文定义片断到一个众所周知的位置路径,而且当最终的应用上下文使用经过classpath*前缀的相同路径被建立时,全部的组件片断都会被自动打包。

_本身的补充:应用于不一样类路径下的相同文件,好比resource1.jar中有一个com.text.rs.jarAppcontext.xml,而resource2.jar中也有一个com.text.rs.jarAppcontext.xml。经过

ApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath*:com/test/rs/jarAppcontext.xml")

能够将两个jar中的同名文件都加载进来。而若是写成下面的代码,就只能找到其中的一个xml文件(顺序取决于jar包的加载顺序)

ApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath:com/test/rs/jarAppcontext.xml");

_

请注意,此通配符特定于在应用上下文的构造函数中的资源路径使用(或当直接使用PathMatcher工具类层次结构时),而且在构造时被解析。与Resource类型自己没有任何关系。不可以使用classpath*:前缀构造实际的Resource,由于一个Resource同时仅仅指定一个资源。

Ant风格模式

当路径位置包含Ant风格模式,例如:

/WEB-INF/*-context.xml
  com/mycompany/**/applicationContext.xml
  file:C:/some/path/*-context.xml
  classpath:com/mycompany/**/applicationContext.xml

解析器遵循更复杂但定义的过程来尝试解析通配符。它为最后一个非通配符段生成资源并从中获取一个URL。若是URL不是以jar:开头或容器特定的变体(例如WebLogic中的zip:,webSphere中的wsjar等),那么将产生一个java.io.File而且用于经过遍历文件系统来解析通配符。在jar URL的状况下,解析器或者获取一个java.net.JarURLConnection或者手动解析jar URL而后遍历jar文件的内容来解析通配符。

对可移植性的影响

若是指定的路径是文件URL(显式的或隐式的),因为基本的ResourceLoader是文件系统资源加载器,通配符能够保证以彻底可移植的方式工做。

若是指定的路径是一个类路径位置,那么解析器经过调用ClassLoader.getResource()获取最后一段非通配符路径段的URL。由于这仅仅是路径的一个节点(不是最后的文件),在这种状况下对于返回什么类型的URL其实是未定义的。在实践中,常常是一个java.io.File对象表明目录,类路径资源解析为一个文件系统位置;或某一类的jar URL,类路径资源解析为jar位置。尽管如此,这种操做仍然存在可移植性问题。

若是从最后一个非通配符段获取到一个jar URL,解析器必须可以从它获取java.net.JarURLConnetcion,或者手动解析jar URL,可以遍历jar的内容,并解析通配符。这在大多数环境中工做良好,可是在一些其它环境会失效;强烈建议jar的通配符资源解析在特定的环境中被全面测试。

Classpath*: 可移植的classpath*前缀

当构建一个基于XML的应用上下文,一个位置字符串可使用特殊的classpath*: 前缀:

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

这个特定的前缀指定全部匹配给定名称的类路径资源必须被获取(在内部,这实际上经过ClassLoader.getResources(...)方法调用完成),而后合并造成最终的应用上下文定义。

通配符的类路径依赖于底层类加载器的getReosources()方法。因为现今大多数应用服务器都提供它们本身的类加载器实现,处理jar文件的行为也许会不一样。用于测试classpath*是否工做的一个简单方法是使用类加载器从类路径下的jar加载文件:getClass().getClassLoader().getResources("<someFileInsideTheJar>")。使用相同名字可是被放置于不一样位置的文件测试。若是返回了不正确的结果,查看应用服务器的文档找到影响类加载器行为的设置。

classpath*: 前缀也能够与位置路径其他部分中的PathMatcher模式组合使用,例如classpath*:META-INF/*-beans.xml。在这种状况下解析策略很简单:ClassLoader.getResources()调用被用于从最后一个非通配符路径段获取全部在类加载器层次结构中匹配的资源,而后上文所述的PathMatcher解析策略用于子路径的通配符资源解析。

有关通配符的其它说明

请注意,classpath*:与Ant风格模式在模式开始前至少一个根目录时有效,除非实际的目标文件位于文件系统。这意味着像classpath*:*.xml这样的模式不能从jar文件中检索文件,而只能从扩展目录的根目录中检索文件。这是由JDK的ClassLoader.getResources()方法的局限性形成的,当传入空字符串时它仅仅返回文件系统位置(代表要搜索的根目录)。

classpath:资源中的Ant风格模式若是要搜索的根包卫浴多个类路径位置不保证可以找到匹配的资源。由于以下的一个资源:

com/mycompany/package1/service-context.xml

也许位于一个路径,可是当路径以下:

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

被解析时,解析器将会使用getResource("com/mycompany")返回的(第一个)URL工做。若是这个基础包节点在多个类加载器位置中存在,实际的资源可能未在其中。所以,在这种状况下优先使用"classpath*:"和相同的Ant风格模式,它会搜索包含根包的全部类路径位置。

7.3 文件系统资源注意事项

未附加到FileSystemApplicationContext的FileSystemResource(即FileSystemApplicationContext并非实际的ResourceLoader),FileSystemResource将按照指望来处理绝对路径和相对路径。相对路径与当前工做路径相关,而绝对路径与文件系统的根目录相关。

因为向后的兼容系的缘由,当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");

在实践中,若是须要绝对文件系统路径,最好放弃将FileSystemResource/FileSystemXmlApplicationContext与绝对路径一块儿使用,使用带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");
相关文章
相关标签/搜索