Spring中的ResourceLoader
获取Resource的方法有两种。一种是通过ResourceLoader
载入资源,另外一种是通过注入。通过ResourceLoader
载入资源的方法如下:
public class Test implements ResourceLoaderAware {
private ResourceLoader resourceLoader;
public void setResourceLoader(ResourceLoader v){...}
public void test(){
Resource resource = resourceLoader.getResource("myfile.xml");
}
}
通过注入载入资源。
// 首先一个普通的类,注意它有一个方法参数类型为URL。
public class MyBean {
public void setLocation(URL resource){}
}
Bean的声明如下。它将一个字符串注入到MyBean
中,但是MyBean
只接受URL
类型,不接受字符串。这时Spring就会将字符串通过ResourceLoading
进行解析得到URL
,再将URL
注入到Bean中。
<bean id="mybean" class="MyBean">
<property name="location" value="myfile.xml"/>
</bean>
稍作改动还可以载入一组资源:
public class MyBean{
public void setLocations(URL[] locations){}
}
<bean id="mybean" class="MyBean">
<property name="locations" value="WEB-INF/webx-*.xml"/>
</bean>
从代码中我们可以看到我们只是指定了文件的相对路径,那么到底相对于哪个目录呢?这跟Spring的ApplicationContext
类型有关系。我们知道Spring有多种ApplicationContext
,不同的ApplicationContext
载入资源时行为是不一样的。ClassPathXmlApplicationContext
会从Classpath中载入资源,FileSystemXmlApplicationContext
会从文件系统中载入资源、XmlWebApplicationContext
会从WEB-INF
中载入资源。另外它们都支持从classpath载入,请看下面的例子,下面几个都可以作为文件名交给ResourceLoader
进行载入。
classpath:myFile.xml
classpath*:/META-INF/my*.xml
Spring的ResourceLoader缺点有以下几个: * 一次只能指定一个ApplicationContext
,无法从多个位置载入资源。 * 每次都要指定资源的绝对路径,移植性不高。 * 没有扩展性,因为只能从文件中载入,如果想从数据库载入就很难做到。
Webx中的ResourceLoading
Webx改进了Spring在资源载入方面的不足,引入了ResourceLoading
服务。它兼容Spring中ResourceLoader
的所有功能,同时增加了很多新的特性。
ResourceLoading
用于统一管理资源,便于资源的载入。资源可以存放在多个地方,可以是文件系统,可以是classpath,也可以是jar包。每种资源位置访问的方式、遍历文件的方式都不一样,这样在访问不同类型的资源时就会很麻烦,而且代码的移植性也不好。ResourceLoading
服务就是将这些不同位置的资源统一成Resource
接口。它的定义如下:
public interface Resource {
InputStream getInputStream();
URL getURL();
File getFile();
boolean exists();
}
ResourceLoading
服务的配置在webx.xml
文件中进行。通过resource-loading
标签就可以实现对资源的统一管理。
下面介绍resource-loading
标签中能包含的子标签。
定义新资源。需要告诉框架资源的名称是什么,文件在哪个位置。下面这个例子表示建立一个文件夹,文件夹中的内容和java的安装目录一致。
<resource pattern="/jdk">
<res-loaders:file-loader basedir="${java.home}" />
</resource>
注入资源的时候只要以jdk目录为开头即可,请看下面的例子。
<property name="location" value="/jdk/lib/tools.jar" />
资源别名。有时候资源名会暴露具体的实现,造成移植性低。比如一个资源名为/WEB-INF/webx.xml
,很明显它就是从WEB-INF
目录中加载的,如果有一天把webx.xml
移动到classpath中,那么这个资源名是很不合理的。所以需要把/WEB-INF/webx.xml
进行重命名,请看下面的例子。
<resource-alias pattern="/myapp/conf" name="/webroot/WEB-INF" />
<resource pattern="/webroot" internal="true">
<res-loaders:webapp-loader />
</resource>
载入资源就可以这样载入:
<property name="location" value="/myapp/conf/webx.xml" />
第二行代码中出现了internal
属性,它的值为true
。表示这个资源只能在webx.xml
内部使用,不能在spring配置文件中使用。
重定向资源。和资源别名类似,区别在于可以指定res-loader
。请看下面的例子,它将template目录中的cms子目录重定向到另外一个目录中,而其他的目录不会受到影响。
<resource-alias pattern="/templates" name="/webroot/templates" />
<resource pattern="/templates/cms">
<res-loaders:file-loader basedir="${cms_root}" />
</resource>
在应用程序中,开发人员无法感知到某个资源被重定向,因此耦合度更低,修改方便。
pattern
属性。在resource-alias
和resource
标签中都需要指定pattern
属性。pattern
可以包含绝对路径和相对路径,绝对路径一定是以/
开头的,而相对路径则相反。下面请看几个例子: *
/aaa/bbb
能匹配/aaa/bbb/ccc
,不能匹配/xxx/aaa/bbb/ccc
*
aaa/bbb
能匹配/aaa/bbb/ccc
,也能匹配/xxx/aaa/bbb/ccc
在pattern中还可以加入通匹配符号。*
表示任意一个目录,**
表示任意多个目录,?
表示一个可有可无的字符。通匹配的内容会按照顺序分别赋值给$1
、$2
等变量,下面请看例子。
<resource-alias pattern="/myapp/conf/**/*.xml" name="/webroot/WEB-INF/$1/$2.xml" />
从多个位置加载资源。有时候配置文件分散在多个文件夹中,但是我们要将这几个文件夹合并在一个资源目录下,那么代码可以这样写:
<resource pattern="/myapp/conf">
<res-loaders:super-loader name="/webroot/WEB-INF" />
<res-loaders:super-loader name="/classpath" />
</resource>
内容过滤。在载入资源的时候还可以对资源文件的内容进行加工。比如下面的例子就是将所有的xml文件转换成xslt。
<resource-filters pattern="test-*.xml">
<res-filters:xslt-filter xslt="/stylesheet.for.test/test.xsl" saveTo="/tempdir" />
</resource-filters>
上面例子中的xslt和saveTo
属性也是webx资源pattern
。
ResourceLoadingService
对象。它能提供一些Spring没有的功能。 获取资源:
Resource resource = resourceLoadingService.getResource("/myapp/conf/myFile.xml");
Resource resource = resourceLoadingService.getResource("/myapp/conf/myFile.xml",
ResourceLoadingService.FOR_CREATE);
获取特定类型的资源:
// 取得资源文件
File file = resourceLoadingService.getResourceAsFile("/myapp/conf/myFile.xml");
// 取得资源 URL
URL url = resourceLoadingService.getResourceAsURL("/myapp/conf/myFile.xml");
// 取得资源输入流
InputStream stream = resourceLoadingService.getResourceAsStream("/myapp/conf/myFile.xml");
资源是否存在:
resourceLoadingService.exists("/myapp/conf/myFile.xml")
枚举资源:
String[] resourceNames = resourceLoadingService.list("/myapp/conf");
Resource[] resources = resourceLoadingService.listResources("/myapp/conf");
调试资源的加载过程:
ResourceTrace trace = resourceLoadingService.trace("/myapp/conf/webx.xml");
for (ResourceTraceElement element : trace) {
System.out.println(element);
}
列出所有可用的pattern。
String[] patterns = resourceLoadingService.getPatterns(true);
在Web环境中ResourceLoadingService
可以通过@Autowired
注入到程序中。在非Web环境中,需要调用ResourceLoadingXmlApplicationContext
对象。
ResourceLoader
对象。它是ResourceLoading
服务的核心。分为多种类型,有FileResourceLoader
、WebappResourceLoader
等。
FileResourceLoader负责从文件系统中加载资源。
<resource pattern="/my/virtual">
<res-loaders:file-loader />
</resource>
这样就会从当前配置文件的目录中加载资源。
指定文件系统位置。
<resource pattern="/my/virtual">
<res-loaders:file-loader basedir="${my.basedir}" />
</resource>
指定多个路径。通过path标签,可以指定相对路径和绝对路径。
<resource pattern="/my/virtual">
<res-loaders:file-loader basedir="...">
<res-loaders:path>relativePathToBasedir</res-loaders:path>
<res-loaders:path type="absolute">c:/absolutePath</res-loaders:path>
</res-loaders:file-loader>
</resource>
WebappResourceLoader负责从当前Web应用中加载资源。
<resource pattern="/my/virtual">
<res-loaders:webapp-loader />
</resource>
ClassPathResourceLoader负责从Classpath路径中载入资源。
<resource pattern="/my/virtual">
<res-loaders:classpath-loader />
</resource>
SuperResourceLoader负责从另外一个ResourceLoader中载入资源。
从/webroot/WEB-INF中加载资源。
<resource pattern="/my/virtual">
<res-loaders:super-loader basedir="/webroot/WEB-INF" />
</resource>
从父容器中载入资源。
<resource pattern="/my/virtual">
<res-loaders:super-loader />
</resource>
Spring容器有继承关系。上面这个例子载入资源时,如果无法从子容器中载入资源,就从父容器中载入。当然也可以改变加载顺序,先从父容器中加载,再从其他位置加载。
<resource pattern="...">
<res-loaders:super-loader />
<res-loaders:file-loader />
</resource>
所有的ResourceLoader
都支持容错,比如SuperResourceLoader
即使在没有父容器的情况下也不会报错。