閱讀768 返回首頁    go 阿裏雲 go 技術社區[雲棲]


《Spring 5 官方文檔》4. 資源(二)

4.6 資源依賴

如果bean本身將通過某種動態過程來確定和提供資源路徑,那麼bean可以使用ResourceLoader接口來加載資源。 j假設以某種方式加載一個模板,其中需要的特定資源取決於用戶的角色。 如果資源是靜態的,那麼完全消除ResourceLoader接口的使用是有意義的,隻需讓bean公開它需要的Resource屬性,那麼它們就會以你所期望的方式被注入。

什麼使得它們輕鬆注入這些屬性,是所有應用程序上下文注冊和使用一個特殊的JavaBeans PropertyEditor,它可以將String路徑轉換為Resource對象。 因此,如果myBean具有“資源”類型的模板屬性,則可以使用該資源的簡單字符串進行配置,如下所示:

<bean  >
<property name="template" value="some/resource/path/myTemplate.txt"/>
</bean>

請注意,資源路徑沒有前綴,因為應用程序上下文本身將用作ResourceLoader,資源本身將通過ClassPathResource,FileSystemResource或ServletContextResource(根據需要)加載,具體取決於上下文的確切類型。

如果需要強製使用特定的資源類型,則可以使用前綴。 以下兩個示例顯示如何強製使用ClassPathResource和UrlResource(後者用於訪問文件係統文件)。

<property name="template" value="classpath:some/resource/path/myTemplate.txt">
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>

4.7 應用上下文和資源路徑

4.7.1 構造應用上下文

(某一特定)應用上下文的構造器通常可以使用字符串或字符串數組所指代的(多個)資源(如 xml 文件)來構造當前上下文。

當指定的位置路徑沒有帶前綴時,那從指定位置路徑創建的 Resource 類型(用於後續加載 bean 定義),取決於所使用應用上下文。舉個列子,如下所創建的 ClassPathXmlApplicationContext :

ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");

會從類路徑加載 bean 的定義,因為所創建的 Resource 實例是 ClassPathResource.但所創建的是 FileSystemXmlApplicationContext 時,

ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");

則會從文件係統加載 bean 的定義,這種情況下,資源路徑是相對工作目錄而言的。

注意:若位置路徑帶有 classpath 前綴或 URL 前綴,會覆蓋默認創建的用於加載 bean 定義的 Resource 類型,比如這種情況下的 FileSystemXmlApplicationContext

ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");

,實際是從類路徑下加載了 bean 的定義。可是,這個上下文仍然是 FileSystemXmlApplicationContext,而不是 ClassPathXmlApplicationContext,在後續作為 ResourceLoader 來使用時,不帶前綴的路徑仍然會從文件係統中加載。

構造 ClassPathXmlApplicationContext 實例 – 快捷方式

ClassPathXmlApplicationContext 提供了多個構造函數,以利於快捷創建 ClassPathXmlApplicationContext 的實例。最好莫不過使用隻包含多個 xml 文件名(不帶路徑信息)的字符串數組和一個 Class 參數的構造器,所省略路徑信息 ClassPathXmlApplicationContext 會從 Class 參數 獲取:

下麵的這個例子,可以讓你對個構造器有比較清晰的認識。試想一個如下類似的目錄結構:

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 使用通配符構造應用上下文

從前文可知,應用上下文構造器的中的資源路徑可以是單一的路徑(即一對一地映射到目標資源);另外資源路徑也可以使用高效的通配符——可包含 classpath*:前綴 或 ant 風格的正則表達式(使用 spring 的 PathMatcher 來匹配)。

通配符機製的其中一種應用可以用來組裝組件式的應用程序。應用程序裏所有組件都可以在一個共知的位置路徑發布自定義的上下文片段,則最終應用上下文可使用 classpath*: 在同一路徑前綴(前麵的共知路徑)下創建,這時所有組件上下文的片段都會被自動組裝。

謹記,路徑中的通配符特定用於應用上下文的構造器,隻會在應用構造時有效,與其 Resource 自身類型沒有任何關係。不可以使用 classpth*:來構造任一真實的 Resource,因為一個資源點一次隻可以指向一個資源。(如果直接使用 PathMatcher 的工具類,也可以在路徑中使用通配符)

Ant 風格模式

以下是一些使用了 Ant 風格的位置路徑:

/WEB-INF/*-context.xml
  com/mycompany/**/applicationContext.xml
  file:C:/some/path/*-context.xml
  classpath:com/mycompany/**/applicationContext.xml

當位置路徑使用了 ant 風格,解釋器會遵循一套複雜且預定義的邏輯來解釋這些位置路徑。解釋器會先從位置路徑裏獲取最靠前的不帶通配符的路徑片段,使用這個路徑片段來創建一個 Resource ,並從 Resource 裏獲取其 URL,若所獲取到 URL 前綴並不是 “jar:”,或其他特殊容器產生的特殊前綴(如 WebLogic 的 zip:,WebSphere wsjar),則從 Resource 裏獲取 java.io.File 對象,並通過其遍曆文件係統。進而解決位置路徑裏通配符;若獲取的是 “jar:”的 URL ,解析器會從其獲取一個 java.net.JarURLConnection 或手動解析此 URL,並遍曆 jar 文件的內容進而解決位置路徑的通配符。

對可移植性的影響

如果指定的路徑已經是文件URL(顯式地或隱含地,因為基本的ResourceLoader是一個文件係統的,那麼通配符將保證以完全可移植的方式工作。

如果指定的路徑是類路徑位置,則解析器必須通過Classloader.getResource()調用獲取最後一個非通配符路徑段URL。 由於這隻是路徑的一個節點(而不是最後的文件),在這種情況下,它實際上是未定義的(在ClassLoader javadocs中)返回的是什麼樣的URL。 實際上,它始終是一個java.io.File,它表示類路徑資源解析為文件係統位置的目錄或某種類型的jar URL,其中類路徑資源解析為一個jar位置。 盡管如此,這種操作仍然存在可移植性問題。

如果為最後一個非通配符段獲取了一個jar URL,解析器必須能夠從中獲取java.net.JarURLConnection,或者手動解析jar URL,以便能夠遍曆該jar的內容,然後解析 通配符。 這將在大多數環境中正常工作,但在其他環境中將會失敗,並且強烈建議您在依賴它之前,徹底地在您的特定環境中徹底測試來自jar的資源的通配符解析。

classpath*: 的可移植性

當構造基於 xml 文件的應用上下文時,位置路徑可以使用 classpath*:前綴:

ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");

classpath*:的使用表示類路徑下所有匹配文件名稱的資源都會被獲取(本質上就是調用了 ClassLoader.getResources(…) 方法),接著將獲取到的資源組裝成最終的應用上下文。

通配符路徑依賴了底層 classloader 的 getResource 方法。可是現在大多數應用服務器提供了自身的 classloader 實現,其處理 jar 文件的形式可能各有不同。要在指定服務器測試 classpath*: 是否有效,簡單點可以使用 getClass().getClassLoader().getResources(“”) 去加載類路徑 jar包裏的一個文件。嚐試在兩個不同的路徑加載名稱相同的文件,如果返回的結果不一致,就需要查看一下此服務器中與 classloader 行為設置相關的文檔。

在位置路徑的其餘部分,classpath*: 前綴可以與 PathMatcher 結合使用,如:” classpath*:META-INF/*-beans.xml”。這種情況的解析策略非常簡單:取位置路徑最靠前的無通配符片段,調用 ClassLoader.getResources() 獲取所有匹配的類層次加載器可加載的的資源,隨後將 PathMacher 的策略應用於每一個獲得的資源(起過濾作用)。

通配符的補充說明

除非所有目標資源都存於文件係統,否則classpath*:和 ant 風格模式的結合使用,都隻能在至少有一個確定根包路徑的情況下,才能達到預期的效果。換句話說,就是像 classpath*:*.xml 這樣的 pattern 不能從根目錄的 jar 文件中獲取資源,隻能從根目錄的擴展目錄獲取資源。此問題的造成源於 jdk ClassLoader.getResources() 方法的局限性——當向 ClassLoader.getResources() 傳入空串時(表示搜索潛在的根目錄),隻能獲取的文件係統的文件位置路徑,即獲取不了 jar 中文件的位置路徑。

如果在多個類路徑上存在所搜索的根包,那使用 classpath: 和 ant 風格模式一起指定的資源不保證找到匹配的資源。因為使用如下的 pattern classpath:com/mycompany/**/service-context.xml
去搜索隻在某一個路徑存在的指定資源com/mycompany/package1/service-context.xml
時,解析器隻會對 getResource(“com/mycompany”) 返回的(第一個) URL 進行遍曆和解釋,則當在多個類路徑存在基礎包節點 “com/mycompany” 時(如在多個 jar 存在這個基礎節點),解析器就不一定會找到指定資源。因此,這種情況下建議結合使用 classpath*: 和 ant 風格模式,classpath*:會讓解析器去搜索所有包含基礎包節點的類路徑。

4.7.3 FileSystemResource 注意事項

FileSystemResource 沒有依附 FileSystemApplicationContext,因為 FileSystemApplicationContext 並不是一個真正的 `ResourceLoader。FileSystemResource 並沒有按約定規則來處理絕對和相對路徑。相對路徑是相對與當前工作而言,而絕對路徑則是相對文件係統的根目錄而言。

然而為了向後兼容,當 FileSystemApplicationContext 是一個 ResourceLoader 實例時,我們做了一些改變 —— 不管 FileSystemResource` 實例的位置路徑是否以 / 開頭, FileSystemApplicationContext 都強製將其作為相對路徑來處理。事實上,這意味著以下例子等效:

ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/context.xml");
ApplicationContext ctx = new FileSystemXmlApplicationContext("/conf/context.xml");

還有:(即使它們的意義不一樣 —— 一個是相對路徑,另一個是絕對路徑。)

FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt");
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt");

實踐中,如果確實需要使用絕對路徑,建議放棄 FileSystemResource / FileSystemXmlApplicationContext 在絕對路勁的使用,而強製使用 file: 的 UrlResource。


// Resource 隻會是 UrlResource,與上下文的真實類型無關
ctx.getResource("file:///some/resource/path/myTemplate.txt");
// 強製 FileSystemXmlApplicationContext 通過 UrlResource 加載資源
ApplicationContext ctx = new FileSystemXmlApplicationContext("file:///conf/context.xml");

轉載自 並發編程網 - ifeve.com


最後更新:2017-05-18 11:02:01

  上一篇:go  sicp 4.2.2小節部分習題
  下一篇:go  sicp 4.2.1兩題