Webx框架:ResourceLoader

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-aliasresource标签中都需要指定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服务的核心。分为多种类型,有FileResourceLoaderWebappResourceLoader等。

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即使在没有父容器的情况下也不会报错。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。