4.7. Application context 和Resource 路徑 |
Spring提供對資源檔的泛型訪問(Generic access),ApplicationContext繼承了org.springframework.core.io.Resource介面,org.springframework.core.io.Resource介面代表著物理存在的任何資源,其繼承於org.springframework.core.io.InputStreamSource;其子類有如下幾種:ByteArrayResource, ClassPathResource, DescriptiveResource, FileSystemResource, InputStreamResource, PortletContextResource, ServletContextResource, UrlResource 。常用的有以下四種:
- ClassPathResource:通過 ClassPathResource 以類路徑的方式進行訪問; 類路(classpath): 顧名思義,即類檔案的輸出路徑。可通過Java Build Path -> Source -> Output folder/Default output folder 查看,如若是java 工程預設是bin ,若是web 工程預設是 classes。
- FileSystemResource:通過 FileSystemResource 以檔案系統絕對路徑相對路徑的方式進行訪問; 如:(1)絕對路徑:Resource resource = context.getResource("file:c:/workspace/oms/config/applicationContext-ibatis.xml")。 (2)相對路徑:但是如果使用相對路徑要注意其根目錄。例如在eclipse中,它的根目錄就是你工程目錄作為你的根目錄。Resource resource = context.getResource("config/applicationContext-ibatis.xml")。 ...然後,由於使用的是XML檔案,所以使用org.springframework.beans.factory.xml.XmlBeanFactory類,XmlBeanFactory實現了org.springframework.beans.factory.BeanFactory 介面,用來讀取定義並建立BeanFactory執行個體。 BeanFactory factory = new XmlBeanFactory(resource); test aa = (test)factory.getBean("applicationListDao1");
- ServletContextResource:通過 ServletContextResource 以相對於Web應用根目錄的方式進行訪問。如:Resource resource = context.getResource("WEB-INF/conf/applicationContext-ibatis.xml")。
- UrlResource :通過java.net.URL來訪問資源,當然它也支援File格式,如“file:”
取得Resource介面的執行個體 後,可以使用getFile(),getInputStream()等方法來操作資源檔
Resource 介面的案例只是資源檔一個抽象代表,指定的資源有可能不存在,可以使用exists()進行測試。
先看一下Resource介面的定義:一下見這個串連:
Spring中的Resource介面
http://blog.csdn.net/myyate/archive/2007/10/10/1818714.aspx
定義檔案存取:
Resource resource = new ClassPathResource("bean.xml");
BeanFactory factory = new XmlBeanFactory(resource);
HelloBean hello = (HelloBean) factory.getBean("helloBean");
這上面的ClassPathResource可以為FileSystemResource,InputStreamResource,ServletContextresource,URLResource;
// bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="helloBeanOfJustin" class="onlyfun.caterpillar.HelloBean">
<property name="helloWord"><value>Hello!Justin!</value></property>
</bean>
<bean id="helloBeanOfcaterpillar" class="onlyfun.caterpillar.HelloBean">
<property name="helloWord"><value>Hello!caterpillar!</value></property>
</bean>
</beans>
操作:
Resource resource = new ClassPathResource("bean.xml");
ListableBeanFactory factory = new XmlBeanFactory(resource);
Map helloBeans = factory.getBeansOfType(HelloBean.class, false, false);
BeanDefinitionRegistry reg = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(reg);
//載入bean
reader.loadBeanDefinitions(new ClassPathResource("bean1.xml"));
reader.loadBeanDefinitions(new ClassPathResource("bean2.xml"));
....
//取得Bean
BeanFactory bf = (BeanFactory) reg;
Object o = bf.getBean("helloBean");
需要org.springframework..beans.factory.support.BeanDefinitionRegistry
和org.springframework..beans.factory.support.DefaultListableBeanFactory.XmlBeanDefinitionReader
Spring的創始者Rod Johnson建議使用ApplicationContext 來取代BeanFactory,在實現ApplicationContext 的類中,最常使用的大概是一下三個。 org.springframework.context.support.FileSystemXmlApplicationContext可指定XML定義檔案的相對路徑或絕對路徑來讀去定義檔案。 org.springframework.context.support.ClassPathXmlApplicationContext從Classpath設定路徑中來讀去定義檔案。 org.springframework.web.context.support.XmlWebApplicationContext在Web應用程式的檔案架構中,指定相對位置來讀去定義檔案。4.7.1. 構造application context
application context構造器通常使用字串或字串數組作為資源(比如組成context定義的XML檔案)的定位路徑。當這樣的定位路徑沒有首碼時,指定的Resource 類型會通過這個路徑來被建立並被用來載入bean的定義,這都取決於你所指定的application context。例如,如果你使用下面的代碼來建立ClassPathXmlApplicationContext :
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
這些Bean的定義會通過classpath載入並使用ClassPathResource。而如果你象下面這麼建立FileSystemXmlApplicationContext:
ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");
這些Bean的定義會通過檔案系統從相對於當前工作目錄中被載入並使用FileSystemResource。
請注意如果定位路徑使用classpath首碼或標準的URL首碼,那它就會覆蓋預設的Resource 類型(FileSystemResource)。因此下面的FileSystemXmlApplicationContext...
ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
...實際上會通過classpath載入其bean定義。然而它仍是個FileSystemXmlApplicationContext。如果後面它被當作ResourceLoader 來使用,那麼任何沒有使用首碼的路徑依然會被當作一個檔案系統路徑。
4.7.1.1. 建立
ClassPathXmlApplicationContext 執行個體 - 簡介
ClassPathXmlApplicationContext 提供了多種構造方法以便於初始化。但其核心是,如果我們僅僅提供由XML檔案名稱組成的字串數組(沒有完整路徑資訊),而且還提供了Class;那麼該ClassPathXmlApplicationContext就會從給定的類中抽取路徑資訊。
希望通過一個樣本把這些闡述清楚。假設有這樣的目錄結構:
com/ foo/ services.xml daos.xml MessengerService.class
由 'services.xml' 和 'daos.xml' 中定義的bean組成的 ClassPathXmlApplicationContext 執行個體會象這樣地來執行個體化...
ApplicationContext ctx = new ClassPathXmlApplicationContext( new String[] {"services.xml", "daos.xml"}, MessengerService.class);
欲瞭解 ClassPathXmlApplicationContext 多種構造方法的細節,請參考它的Javadocs。
4.7.2. Application context構造器中資源路徑的萬用字元
Application context構造器中資源路徑的值可以是簡單的路徑(就像上面的那樣),即一對一映射到一個目標資源;或者可以包含特殊的"classpath*:"首碼和Ant風格的Regex(用Spring的 PathMatcher 工具來匹配)。後面的二者都可以使用萬用字元。
該機制的一個用處就是做組件類型的應用組裝。所有的組件都可以用通用的定位路徑“發布”context定義片斷,這樣當使用相同的 classpath*: 首碼建立最終的application context時,所有的組件片斷都會被自動裝入。
請注意,這個萬用字元只在application context構造器的資源路徑中 (或直接在類的層次中使用 PathMatcher 工具時)有效,它會在構造時進行解析。這與 Resource 類型本身沒有關聯。因為同一時刻只能指向一個資源,所以不能使用 classpath*: 首碼來構造實際的Resource。<也就是不能在像這樣語句中使用classpath*:首碼, Resource resource = context.getResource("WEB-INF/conf/applicationContext-ibatis.xml")。是否?>
4.7.2.1. Ant風格的pattern
在包含Ant風格的pattern時,例如:
/WEB-INFapplicationContext.xml file:C:/some/pathapplicationContext.xml
解析器會進行一個預先定義的複雜的過程去試圖解析萬用字元。它根據路徑中最後一個非萬用字元片斷產生一個Resource並從中獲得一個URL。如果這個URL不是一個"jar:" URL或特定容器的變數(例如WebLogic中的 "zip:",WebSphere中的"wsjar"等等), 那麼可以從中獲得一個java.io.File,並用它從檔案系統中解析萬用字元。如果是一個jar URL,解析器可以從中取得一個 java.net.JarURLConnection,或者手工解析該jar URL, 隨後遍曆jar檔案以解析萬用字元。
4.7.2.1.1. 潛在的可移植性
如果給定的路徑已經是一個檔案URL(可以是顯式的或者是隱式的),由於基本的ResourceLoader是針對檔案系統的,那麼萬用字元一定能夠移植。
如果給定的路徑是一個classpath的位置,那麼解析器必須通過一個 Classloader.getResource() 調用獲得最後一個非萬用字元路徑片斷的URL。因為這僅僅是一個路徑的節點(不是最終的檔案),所以它並未確切定義(在 ClassLoader Javadocs裡) 此處究竟會返回什麼類型的URL。一般情況下,當classpath資源解析為一個檔案系統位置時,返回一個代表目錄的 java.io.File;當解析為jar位置時,返回某類jar URL。當然,這個操作涉及到可移植性。
如果從最後一個非萬用字元片斷中獲得一個jar URL,那麼解析器一定能從中取得一個 java.net.JarURLConnection,或者手動解析jar URL以遍曆jar檔案,從而解析萬用字元。這一操作在大多數環境中能正常工作,不過也有例外,因此我們強烈建議特定環境中的jar資源萬用字元解析應在正式使用前要經過徹底測試。
《續》
4.7.2.2.
classpath*: 首碼
當構造基於XML的application context時,路徑字串可能使用特殊的 classpath*: 首碼:
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
此首碼表示所有與給定名稱匹配的classpath資源都應該被擷取(其中,這經常會在調用 ClassLoader.getResources(...)) 時發生),並接著將那些資源全並成最終的application context定義。
Classpath*: 的可移植性
帶萬用字元的classpath依賴於底層classloader的 getResources() 方法。現在大多數的應用伺服器提供自己的classloader實現,它們在處理jar檔案時的行為也許會有所不同。要測試 classpath*: 是否有效,可以簡單地用classloader從classpath中的jar檔案裡載入一個檔案: getClass().getClassLoader().getResources("<someFileInsideTheJar>")。針對兩個不同位置但有相同名字的檔案來運行測試。如果結果不對,那麼就查看一下應用伺服器的文檔,特別是那些可能影響classloader行為的設定。
"classpath*:"首碼也能在位置路徑的其他部分結合PathMatcher pattern一起使用,例如"classpath*:META-INFservice-context.xml
解析器會排除getResource("com/mycompany");返回的(第一個)URL。如果這個基礎包節點存在於多個classloader位置,最終要找的資源未必會被發現。因此在這種情況中最好在這個Ant風格的pattern中使用"classpath*:",這樣就會搜尋包含根包在內所有類路徑。
4.7.3.
FileSystemResource 提示
一個並沒有與 FileSystemApplicationContext 綁定的 FileSystemResource(也就是說FileSystemApplicationContext 並不是真正的ResourceLoader),會象你期望的那樣分辨絕對和相對路徑。相對路徑是相對於當前的工作目錄,而絕對路徑是相對與檔案系統的根目錄。
為了向前相容的目的,當 FileSystemApplicationContext 是個 ResourceLoader 時它會發生變化。FileSystemApplicationContext 會簡單地讓所有綁定的 FileSystemResource 執行個體把絕對路徑都當成相對路徑,而不管它們是否以反斜線開頭。也就是說,下面的含義是相同的:
ApplicationContext ctx = new FileSystemClassPathXmlApplicationContext("conf/context.xml");
ApplicationContext ctx = new FileSystemClassPathXmlApplicationContext("/conf/context.xml");
下面的也一樣:(雖然把它們區分開來也很有意義,但其中的一個是相對路徑而另一個則是絕對路徑)。
FileSystemXmlApplicationContext ctx = ...;ctx.getResource("some/resource/path/myTemplate.txt");
FileSystemXmlApplicationContext ctx = ...;ctx.getResource("/some/resource/path/myTemplate.txt");
實際上如果的確需要使用絕對路徑,那你最好就不要使用 FileSystemResource 或 FileSystemXmlApplicationContext來確定絕對路徑。我們可以通過使用 file: URL首碼來強制使用UrlResource。
// actual context type doesn't matter, the Resource will always be UrlResourcectx.getResource("file:/some/resource/path/myTemplate.txt");
// force this FileSystemXmlApplicationContext to load it's definition via a UrlResourceApplicationContext ctx = new FileSystemXmlApplicationContext("file:/conf/context.xml");