?
? ????? PHP ??? ???? ??? ?? ??
application context構(gòu)造器通常使用字符串或字符串?dāng)?shù)組作為資源(比如組成context定義 的XML文件)的定位路徑。
當(dāng)這樣的定位路徑?jīng)]有前綴時(shí),指定的 Resource
類型會(huì)通過這個(gè)路徑來被創(chuàng)建并被用來載入bean的定義,這都取決于你所指定的application context。例如,如果你使用下面的代碼來創(chuàng)建ClassPathXmlApplicationContext
:
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
這些Bean的定義會(huì)通過classpath載入并使用ClassPathResource
。而如果你象下面這么創(chuàng)建FileSystemXmlApplicationContext
:
ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");
這些Bean的定義會(huì)通過文件系統(tǒng)從相對于當(dāng)前工作目錄中被載入。
請注意如果定位路徑使用classpath前綴或標(biāo)準(zhǔn)的URL前綴,那它就會(huì)覆蓋默認(rèn)的Resource
類型。因此下面的FileSystemXmlApplicationContext
...
ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
...實(shí)際上會(huì)通過classpath載入其bean定義。然而它仍是個(gè)FileSystemXmlApplicationContext
。
如果后面它被當(dāng)作ResourceLoader
來使用,那么任何沒有使用前綴的路徑依然會(huì)被當(dāng)作一個(gè)文件系統(tǒng)路徑。
ClassPathXmlApplicationContext
提供了多種構(gòu)造方法以便于初始化。但其核心是,如果我們僅僅提供由XML文件名組成的字符串?dāng)?shù)組(沒有完整路徑信息), 而且還提供了Class
;那么該ClassPathXmlApplicationContext
就
會(huì)從給定的類中抽取路徑信息。
希望通過一個(gè)示例把這些闡述清楚。假設(shè)有這樣的目錄結(jié)構(gòu):
com/ foo/ services.xml daos.xml MessengerService.class
由 'services.xml'
和 'daos.xml'
中定義的bean組成的
ClassPathXmlApplicationContext
實(shí)例會(huì)象這樣地來實(shí)例化...
ApplicationContext ctx = new ClassPathXmlApplicationContext( new String[] {"services.xml", "daos.xml"}, MessengerService.class);
欲了解 ClassPathXmlApplicationContext
多種構(gòu)造方法的細(xì)節(jié),請參考它的Javadocs。
Application context構(gòu)造器中資源路徑的值可以是簡單的路徑(就像上面的那樣),即一對一映射到一個(gè)目標(biāo)資源;
或者可以包含特殊的"classpath*:"前綴和Ant風(fēng)格的正則表達(dá)式(用Spring的 PathMatcher
工具來匹配)。
后面的二者都可以使用通配符。
該機(jī)制的一個(gè)用處就是做組件類型的應(yīng)用組裝。所有的組件都可以用通用的定位路徑“發(fā)布”context定義片斷,
這樣當(dāng)使用相同的 classpath*:
前綴創(chuàng)建最終的application context時(shí),所有的組件片斷都會(huì)被自動(dòng)裝入。
請注意,這個(gè)通配符只在application context構(gòu)造器的資源路徑中
(或直接在類的層次中使用 PathMatcher
工具時(shí))有效,
它會(huì)在構(gòu)造時(shí)進(jìn)行解析。這與 Resource
類型本身沒有關(guān)聯(lián)。因?yàn)橥粫r(shí)刻只能指向一個(gè)資源,所以不能使用
classpath*:
前綴來構(gòu)造實(shí)際的Resource
。
在包含Ant風(fēng)格的pattern時(shí),例如:
/WEB-INFapplicationContext.xml file:C:/some/pathapplicationContext.xml
解析器會(huì)進(jìn)行一個(gè)預(yù)先定義的復(fù)雜的過程去試圖解析通配符。
它根據(jù)路徑中最后一個(gè)非通配符片斷產(chǎn)生一個(gè)Resource并從中獲得一個(gè)URL。
如果這個(gè)URL不是一個(gè)"jar:" URL或特定容器的變量(例如WebLogic中的
"zip:
",WebSphere中的"wsjar
"等等),
那么可以從中獲得一個(gè)java.io.File
,
并用它從文件系統(tǒng)中解析通配符。如果是一個(gè)jar URL,解析器可以從中取得一個(gè)
java.net.JarURLConnection
,或者手工解析該jar URL,
隨后遍歷jar文件以解析通配符。
如果給定的路徑已經(jīng)是一個(gè)文件URL(可以是顯式的或者是隱式的), 由于基本的ResourceLoader是針對文件系統(tǒng)的,那么通配符一定能夠移植。
如果給定的路徑是一個(gè)classpath的位置,那么解析器必須通過一個(gè)
Classloader.getResource()
調(diào)用獲得最后一個(gè)
非通配符路徑片斷的URL。因?yàn)檫@僅僅是一個(gè)路徑的節(jié)點(diǎn)(不是最終的文件),
所以它并未確切定義(在 ClassLoader
Javadocs里)
此處究竟會(huì)返回什么類型的URL。一般情況下,當(dāng)classpath資源解析為一個(gè)文件系統(tǒng)位置時(shí),
返回一個(gè)代表目錄的 java.io.File
;當(dāng)解析為jar位置時(shí),
返回某類jar URL。當(dāng)然,這個(gè)操作涉及到可移植性。
如果從最后一個(gè)非通配符片斷中獲得一個(gè)jar URL,那么解析器一定能從中取得一個(gè)
java.net.JarURLConnection
,或者手動(dòng)解析jar URL以遍歷jar文件,
從而解析通配符。這一操作在大多數(shù)環(huán)境中能正常工作,不過也有例外,
因此我們強(qiáng)烈建議特定環(huán)境中的jar資源通配符解析應(yīng)在正式使用前要經(jīng)過徹底測試。
當(dāng)構(gòu)造基于XML的application context時(shí),路徑字符串可能使用特殊的 classpath*:
前綴:
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
此前綴表示所有與給定名稱匹配的classpath資源都應(yīng)該被獲取(其中,這經(jīng)常會(huì)在調(diào)用 ClassLoader.getResources(...)
) 時(shí)發(fā)生),并接著將那些資源全并成最終的application context定義。
帶通配符的classpath依賴于底層classloader的 getResources()
方法。
現(xiàn)在大多數(shù)的應(yīng)用服務(wù)器提供自己的classloader實(shí)現(xiàn),它們在處理jar文件時(shí)的行為也許會(huì)有所不同。
要測試 classpath*:
是否有效,可以簡單地用classloader從classpath中的jar文件里加載一個(gè)文件:
getClass().getClassLoader().getResources("<someFileInsideTheJar>")
。
針對兩個(gè)不同位置但有相同名字的文件來運(yùn)行測試。如果結(jié)果不對,那么就查看一下應(yīng)用服務(wù)器的文檔,
特別是那些可能影響classloader行為的設(shè)置。
" 解析器會(huì)排除classpath*:
"前綴也能在位置路徑的其他部分結(jié)合PathMatcher
pattern一起使用,例如"classpath*:META-INFservice-context.xml
getResource("com/mycompany")
;返回的(第一個(gè))URL。
如果這個(gè)基礎(chǔ)包節(jié)點(diǎn)存在于多個(gè)classloader位置,最終要找的資源未必會(huì)被發(fā)現(xiàn)。
因此在這種情況中最好在這個(gè)Ant風(fēng)格的pattern中使用"classpath*:
",
這樣就會(huì)搜索包含根包在內(nèi)所有類路徑。
一個(gè)并沒有與 FileSystemApplicationContext
綁定的
FileSystemResource
(也就是說FileSystemApplicationContext
并不是真正的ResourceLoader
),會(huì)象你期望的那樣分辨絕對和相對路徑。
相對路徑是相對于當(dāng)前的工作目錄,而絕對路徑是相對與文件系統(tǒng)的根目錄。
為了向前兼容的目的,當(dāng) FileSystemApplicationContext
是個(gè)
ResourceLoader
時(shí)它會(huì)發(fā)生變化。FileSystemApplicationContext
會(huì)簡單地讓所有綁定的 FileSystemResource
實(shí)例把絕對路徑都當(dāng)成相對路徑,
而不管它們是否以反斜杠開頭。也就是說,下面的含義是相同的:
ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/context.xml");
ApplicationContext ctx = new FileSystemXmlApplicationContext("/conf/context.xml");
下面的也一樣:(雖然把它們區(qū)分開來也很有意義,但其中的一個(gè)是相對路徑而另一個(gè)則是絕對路徑)。
FileSystemXmlApplicationContext ctx = ...; ctx.getResource("some/resource/path/myTemplate.txt");
FileSystemXmlApplicationContext ctx = ...; ctx.getResource("/some/resource/path/myTemplate.txt");
實(shí)際上如果的確需要使用絕對路徑,那你最好就不要使用 FileSystemResource
或 FileSystemXmlApplicationContext
來確定絕對路徑。我們可以通過使用 file:
URL前綴來強(qiáng)制使用UrlResource
。
// actual context type doesn't matter, the Resource
will always be UrlResource
ctx.getResource("file:/some/resource/path/myTemplate.txt");
// force this FileSystemXmlApplicationContext to load it's definition via a UrlResource
ApplicationContext ctx =
new FileSystemXmlApplicationContext("file:/conf/context.xml");