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