Spring3中的Resource

文章基于Spring3.1版本


1、概述

在日常程序开发中,处理外部资源是很繁琐的事情,因为我们可能需要处理不同类型的资源如URL资源、File资源、ClassPath资源等。另外处理这些资源需要使用不同的接口,但步骤都是类似的(打开、读写、关闭等),因此如果能抽象出一个统一的接口来对这些底层资源进行统一访问,会使系统更加简洁,都是对不同的底层资源使用同一个接口进行访问。
Spring 就提供一个Resource接口来统一这些底层资源一致的访问,而且提供了一些便利的接口。


2、接口定义

2.1、继承体系

下面来看一下Spring3.1中Resource的继承体系:

Spring3中的Resource_第1张图片


2.2、接口分析

从源码看一下InputStreamSource接口和Resource接口都定义哪些方法。

InputStreamSource接口:

package org.springframework.core.io;  
  
import java.io.IOException;  
import java.io.InputStream;  
  
public interface InputStreamSource {  
    InputStream getInputStream() throws IOException;  
}  

该接口只定义了一个方法

getInputStream:返回一个新的资源对应的java.io. InputStream字节流,调用者在使用完毕后必须关闭该资源。

Resource接口:

package org.springframework.core.io;  
  
import java.io.File;  
import java.io.IOException;  
import java.io.InputStream;  
import java.net.URI;  
import java.net.URL;  
  
public interface Resource extends InputStreamSource {  
    boolean exists();  
    boolean isReadable();  
    boolean isOpen();  
    URL getURL() throws IOException;  
    URI getURI() throws IOException;  
    File getFile() throws IOException;  
    long contentLength() throws IOException;  
    long lastModified() throws IOException;  
    Resource createRelative(String relativePath) throws IOException;  
    String getFilename();  
    String getDescription();  
}  

Resource 继承自InputStreamSource接口,并提供一些便利方法:

  • exists:返回当前Resource代表的底层资源是否存在,true表示存在。
  • isReadable:返回当前Resource代表的底层资源是否可读,true表示可读。
  • isOpen:当前Resource代表的底层资源是否已经打开,如果返回true,则只能被读取一次然后关闭以避免内存泄漏;常见的Resource实现一般返回false,只有InputStreamResource例外。
  • getURL:如果当前Resource代表的底层资源能由java.util.URL代表,则返回该URL,否则抛出IOException。
  • getURI:如果当前Resource代表的底层资源能由java.util.URI代表,则返回该URI,否则抛出IOException。
  • getFile:如果当前Resource代表的底层资源能由java.io.File代表,则返回该File,否则抛出IOException。
  • contentLength:返回当前Resource代表的底层文件资源的长度,一般是值代表的文件资源的长度。
  • lastModified:返回当前Resource代表的底层资源的最后修改时间。
  • createRelative:用于创建相对于当前Resource代表的底层资源的资源,比如当前Resource代表文件资源“d:/test/”则createRelative(“test.txt”)将返回表文件资源“d:/test/test.txt”Resource资源。
  • getFilename:返回当前Resource代表的底层文件资源的文件路径,比如File资源“file://d:/test.txt”将返回“d:/test.txt”,而URL资源http://www.javass.cn将返回“”,因为只返回文件路径。
  • getDescription:当前Resource代表的底层资源的描述信息,通常就是资源的全路径(实际文件名或实际URL地址)。


2.3、Resource实现

  • UrlResource

UrlResource内部封装了一个java.net.URL,可以用于访问可通过URL访问的任何资源对象,支持的URL协议如下:

HTTP:通过标准的HTTP协议,例如:new UrlResource("http://xxxx.xxx,xxx");

FTP:通过FTP协议访问资源,例如:new UrlResource("ftp://xxx/xxx/xxx");

File:通过文件协议访问本地资源,例如:new UrlResource("file:c:/xxx/xxx");跟FileSystemResource作用一样。

  • ClassPathResource

ClassPathResource代表一个需要从类路径获取的Resource,它可以使用给定的类加载器、给定类或者默认的当前线程上下文类加载器来加载资源。

  • FileSystemResource

FileSystemResource内部封装了java.io.File,getInputStream方法返回的是该File对象对应的文件流FileInputStream。

  • ServletContextResource

代表Web应用中的资源,可以翻译相对于Web应用根目录的相对路径。

  • InputStreamResource

InputStreamResource代表java.io.InputStream字节流,getInputStream方法直接返回该字节流,与其它的Resource实现不同的是isOpen方法永远返回true,所以不能多次读取该字节流,如果可能,最好使用ByteArrayResource或者其它任何基于文件的Resource实现来代替。

  • ByteArrayResource

ByteArrayResource代表byte[]数组资源,同样内部封装了一个byte数组,getInputStream方法将返回给定byte数组对应的ByteArrayInputStream。在加载给定字节型数组内容时比较有用,不需要再去使用一次性的InputStreamResource。


3、ResourceLoader接口

public interface ResourceLoader {
    Resource getResource(String location);
}

所有的ApplicationContext都实现了ResourceLoader接口(通过继承DefaultResourceLoader类,该类实现了ResourceLoader接口),所以都可以用来获取Resource实例。

当我们调用ApplicationContext的getResource方法获取Resource时,如果参数location没有指定前缀,得到的Resource类型根据ApplicationContext的类型来定,比如调用ClassPathXmlApplicationContext实例的getResource方法返回的就是ClassPathResource,如果同样的方法在FileSystemXmlApplicationContext实例上调用,则返回的是FileSystemResource。

另外,我们以指定前缀来强行得到指定的Resource,而不用考虑当前是哪种类型的ApplicationContext,比如在FileSystemXmlApplicationContext上这样调用:

ctx.getResource("classpath:some/resource/path/myTemplate.txt")  

返回的将是ClassPathResource。


4、ResourceLoaderAware接口

ResourceLoaderAware是一个标记接口,用于通过ApplicationContext上下文获取Bean的时候为实现该接口的Bean注入ResourceLoader。

public interface ResourceLoaderAware {
    void setResourceLoader(ResourceLoader resourceLoader);
}

在Spring2.5之后可以使用@ResourceLoader注解来代替实现ResourceLoaderAware接口。


5、Resources注入

假设我们需要在Bean使用某个资源,当然可以采用第4步说的实现ResourceLoaderAware接口得到ResourceLoader,然后使用它来得到资源。其实Spring还提供了资源注入来简化这一操作,就是PropertyEditor这个JavaBean,它可以将String类型的路径转化为Resource对象,所以我们可以将资源注入到Bean中。

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

上面的示例中path是没有前缀的,所以得到什么类型的Resource是根据ApplicationContext的类型来定的,我们也可以采用加前缀的方式来得到指定类型的Resource实例。

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

这样前者将会注入ClassPathResource实例,而后者将会注入UrlResource实例。


6、ApplicationContext与Resource

6.1、构造函数

一个特定的ApplicationContext构造函数通常会接收一个字符串或者一个字符串数组作为Resource资源路径,跟ResourceLoader接口一样,如果我们没有加前缀,内部构建什么类型的Resource来加载BeanDefinition是根据ApplicationContext的类型来定的,比如:

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

这种情况,BeanDefinition为从classpath加载,构建ClassPathResource实例,同样的情况会发生在其它类型的ApplicationContext上如FileSystemXmlApplicationContext构建时会从文件系统加载BeanDefinition。

需要注意的是,如果我们在路径上使用的前缀,如classpath或者其它的URL前缀,这样会覆盖ApplicationContext构造时使用的默认Resource类型,比如:

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

这个例子中,虽然我们构造的是FileSystemXmlApplicationContext,但实际会从类路径下加载BeanDefinition。虽然如此,但它仍然是个FileSystemXmlApplicationContext实例,如果后来还是要用它作为ResourceLoader,调用getResource方法时如果参数不加前缀,仍然会从文件系统下加载资源。


6.2、通配符

Spring还支持通配符加载匹配的一批Resource资源。


6.2.1、Ant模式的通配符

“?”:匹配一个字符,比如“application-context-?.xml“可以匹配”application-context-a.xml“;
"*":匹配零或多个字符,比如“application-context-*.xml”可以匹配“application-context-service.xml”、“application-context-dao.xml”等资源;
“**”:匹配零或多个目录,比如“/com/**/service/application-context-?.xml”可以匹配“/com/service/application-context-?.xml”、““/com/zyh/service/application-context-?.xml””等资源。


6.2.2、“classpath*:”前缀

Spring在加载类路径资源时提供了前缀“classpath:”的来支持加载一个Resource,其实Spring还提供了另一个前缀“classpath*:”来支持加载所有匹配的类路径Resource资源,比如:

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

将会加载类路径下文件及jar包中文件。

你可能感兴趣的:(java,spring)