Java虽然提供了java.net.URL类和各种URL前缀处理程序来负责处理对各种资源的访问,但对于低级别资源的访问来说还是不够充分。例如,没有标准化的实现可用于访问需要从类路径中获取或者相对于一个ServletContext的资源;也没有检查所指向的资源是否存在的标准方法。因此Spring提供了Resource来达成目的。
类作用
Resource是Spring的资源抽象,本质上是JDKjava.net.URL类的一个功能更丰富的版本。Resource可以以透明的方式从几乎任何位置获得低级资源,包括从类路径、文件系统位置、可以用标准URL描述的任何位置以及其他一些变体。如果资源位置字符串是一个没有任何特殊前缀的简单路径,那么这些资源的来源是特定的,并且适合于实际的应用程序容器类型。
Spring本身广泛使用Resource,其实应用容器(ApplicationContext)本身就是个ResourceLoader,可用于加载Resource对象。提供给ApplicationContext构造函数的一个或多个位置路径实际上是资源字符串,并且以简单的形式根据特定的容器实现进行适当处理。例如,ClassPathXmlApplicationContext将简单的位置路径视为类路径位置。您还可以使用带有特殊前缀的位置路径(资源字符串)来强制从类路径或URL加载定义,而不考虑实际的容器类型。
虽然Spring本身使用Resource接口很多,但它实际上是一个非常方便的通用实用程序类,用于在自己的代码中访问资源,即使你的代码不知道或关心Spring的任何其他部分。虽然这会使你的代码与Spring耦合,但它只会将其与这个小集合的实用程序类耦合,这些实用程序类可以作为URL的更强大的替代品,并且可以被认为是用于此目的的任何其他库的等效物。
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();
//返回资源描述,通常是文件全路径名或完整的URL
String getDescription();
}
//父接口
public interface InputStreamSource {
//定位并打开资源,返回输入流
InputStream getInputStream() throws IOException;
}
实际上,Resource本身作为一个通用工具类,在我们的应用代码中可直接使用,可以非常方便访问资源。
注:由于Resource主要就是用于访问资源,因此只有读取相关的方法,没有修改/写入的方法。针对有修改需求的资源,通过子类接口WritableResource提供。
类关系图
基于Resource的目的,spring构建按了整个Resource实现的体系,其类关系图如下:
1、Resouce继承了InputStreamSource,通过来源getInputStream()统一转换为输入流InputStream,以便访问。
2、AbstractResource是Resource的基础实现,除了getInputStream由子类实现外,其它资源的访问等典型行为均统一实现了。
3、AbstractResource的子孙类就是各种具体资源的实现,包括:
AbstractFileResolvingResource:实现对URL/URI类资源访问
UrlResource:实现java.net.URL定位的资源访问,支持"file:"协议
FileUrlResource:是在UrlResource基础上增加修改资源能力
ClassPathResource:实现通过classpath寻址的资源访问
FileSystemResource:实现java.io.File或java.nio.file.Path定位的资源访问
ByteArrayResource:实现来自ByteArray的资源访问
InputStreamResource:实现来自InputStream的资源访问
PathResource:实现java.nio.file.Path定位的资源访问
VfsResource:实现基于JBoss VFS定位的资源访问
4、WritableResource是增加Resource可写入能力,支持的类包括:FileUrlResource、FileSystemResource、PathResource。
类实现
UrlResource
UrlResource针对来自java.net.URL类资源的对象,如文件、HTTPS目标、FTP目标等。所有URL都是一个标准化的字符串,一般通过标准化前缀来指示URL类型。标准化前缀包括:"file:"表示通过文件系统路径访问资源;“https:"或“http:"表示通过https/http协议访问资源;”ftp:“表示通过ftp协议访问资源等。
ClassPathResource
ClassPathResource类表示从类路径(ClassPath)获得的资源。
FileSystemResource
FileSystemResource是针对访问java.io.File句柄或java.nio.file.Path句柄资源的Resource实现。
java.nio.path.path句柄资源的访问见PathResource。
FileSystemResource的资源路径总是相对于应用根目录,无论第一个字符是否有”/",如下面两个语句是等效的:
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/context.xml");
ApplicationContext ctx =
new FileSystemXmlApplicationContext("/conf/context.xml");
PathResource
PathResource是针对访问java.nio.file.Path句柄的Resource实现,通过Path API执行所有操作和转换。它支持解析为文件和URL,还实现了扩展的WritableResource接口。
PathResource实际上是FileSystemResource的纯java.nio.path.path替代方案。
ServletContextResource
ServletContextResource是针对访问ServletContext资源的Resource实现,用于相关web应用程序根目录中的相对路径。
InputStreamResource
InputStreamResource是针对访问已打开的InputStream的Resource实现。
ByteArrayResource
ByteArrayResource是把一个给定字节数组当作资源访问的Resource实现。它为给定的字节数组创建一个ByteArrayInputStream。
资源加载
资源加载器
Resource只管负责资源访问,资源如何加载进来就要靠加载类完成。
资源加载器是个统一接口:
public interface ResourceLoader {
Resource getResource(String location);
ClassLoader getClassLoader();
}
ResourceLoader实现类关系图如下:
ResourceLoader:通用加载资源接口。
DefaultResourceLoader:通用资源加载实现(阅读下getResource(String location)实现)。有特殊加载方式通过子类实现,包括:ClassRelativeResourceLoader、FileSystemResourceLoader。
所有类型的应用容器都实现了ResourceLoader。
ResourcePatternResolver-路径通配符处理
ResourcePatternResolver接口是ResourceLoader接口的扩展,该接口定义了把包含"classpath*:"或Ant正则表达式通配符的资源位置下的所有资源解析为Resource对象。
1、"classpath*:"表示类路径下所有目录或jar包中,与给定名称匹配的类路径资源都进行加载。
2、Ant正则表达式通配符有三种:
? :匹配任何单字符
* :匹配0或者任意数量的字符
**:匹配0或者更多的目录
ResourcePatternResolver接口如下:
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException;
}
PathMatchingResourcePatternResolver是接口ResourcePatternSolver的独立实现,可脱离容器之外使用。
任何标准ApplicationContext中的默认ResourceLoader实际上都是PathMatchingResourcePatternResolver的一个实例。
应用访问资源
直接获取使用
引用程序可利用Resource获得使用资源的能力。举例代码如下:
/*依据ctx的容器类型加载资源,并返回适当的Resource对象
ClassPathXmlApplicationContext:返回ClassPathResource
FileSystemXmlApplicationContext:返回FileSystemResource
WebApplicationContext:返回ServletContextResource
*/
Resource res= ctx.getResource("some/resource/path/myTemplate.txt");
//在classpath下查找资源并加载
Resource res= ctx.getResource("classpath:some/resource/path/myTemplate.txt");
//以URL方式(file协议)加载资源
Resource res= ctx.getResource("file:///some/resource/path/myTemplate.txt");
//以URL方式(https协议)加载资源
Resource res= ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
Aware方式获取
要使用资源的应用类实现ResourceLoaderAware接口,应用启动过程中,SF会自动把ResourceLoader接入到到应用类。代码如下:
public interface ResourceLoaderAware {
void setResourceLoader(ResourceLoader resourceLoader);
}
//应用
public class TestResource implements ResourceLoaderAware {
private ResourceLoader resourceLoader ;
void setResourceLoader(ResourceLoader resourceLoader){
this.resourceLoader = resourceLoader ;
}
...
}
应用类也可以实现ApplicationContextAware,因为ApplicationContext就是本身的祖先类就是ResourceLoader。
静态资源bean
如果bean本身将通过某种动态过程来确定和提供资源。如果bean是动态资源(如考虑加载某种类型的模板,其中所需的特定资源取决于用户的角色),可使用ResourceLoader或ResourcePatternResolver接口加载资源。如果资源是静态的,可完全消除对ResourceLoader接口(或ResourcePatternResolver接口)的使用,可通过bean配置直接注入,而且可多种方式。举例如下:
注入方式1:
public class MyBean {
private Resource resource;
public setResource(Resource resource) {
this.resource= resource;
}
// ...
}
配置如下:
<bean id="myBean" class="example.MyBean">
<property name="resource" value="some/resource/path/myResource.txt"/>
</bean>
注入方式2:
@Component
public class MyBean {
private final Resource[] resources;
//resources.path是在应用属性文件中配置的
public MyBean(@Value("${resources.path}") Resource[] resources) {
this.resources= resources;
}
// ...
}