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即使在沒有父容器的情況下也不會報錯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章