資源
本章介紹 Spring 如何處理資源以及如何在 Spring 中使用資源。它包括以下主題
簡介
不幸的是,Java 標準的 java.net.URL
類以及用於各種 URL 字首的標準處理程式對於訪問所有低階資源來說並不完全足夠。例如,沒有可用於訪問需要從類路徑或相對於 ServletContext
獲取的資源的標準化 URL
實現。雖然可以為專門的 URL
字首註冊新的處理程式(類似於用於 http:
等字首的現有處理程式),但這通常相當複雜,並且 URL
介面仍然缺少一些需要的功能,例如用於檢查所指向資源是否存在的方法。
Resource
介面
Spring 的 Resource
介面位於 org.springframework.core.io.
包中,旨在成為一個更強大的介面,用於抽象訪問低階資源。以下列表提供了 Resource
介面的概述。有關更多詳細資訊,請參閱 Resource
的 Javadoc。
public interface Resource extends InputStreamSource {
boolean exists();
boolean isReadable();
boolean isOpen();
boolean isFile();
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
ReadableByteChannel readableChannel() throws IOException;
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String relativePath) throws IOException;
String getFilename();
String getDescription();
}
如 Resource
介面的定義所示,它擴充套件了 InputStreamSource
介面。以下列表顯示了 InputStreamSource
介面的定義
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
Resource
介面中一些最重要的方法包括
-
getInputStream()
:定位並開啟資源,返回一個InputStream
用於從資源讀取。每次呼叫都應該返回一個新的InputStream
。呼叫者有責任關閉流。 -
exists()
:返回一個boolean
值,指示此資源是否實際物理存在。 -
isOpen()
:返回一個boolean
值,指示此資源是否表示一個帶有開啟流的控制代碼。如果為true
,則InputStream
不能多次讀取,只能讀取一次,然後必須關閉以避免資源洩漏。除了InputStreamResource
之外,所有常規資源實現都返回false
。 -
getDescription()
:返回此資源的描述,用於處理資源時的錯誤輸出。這通常是資源的完全限定檔名或實際 URL。
其他方法允許您獲取表示資源的實際 URL
或 File
物件(如果底層實現相容並支援該功能)。
Resource
介面的一些實現還實現了擴充套件的 WritableResource
介面,用於支援寫入的資源。
Spring 本身廣泛使用 Resource
抽象,在許多需要資源的方法簽名中將其作為引數型別。某些 Spring API 中的其他方法(例如各種 ApplicationContext
實現的建構函式)接受一個 String
引數,該引數在未加修飾或簡單形式下用於建立適合該上下文實現的 Resource
,或者透過 String
路徑上的特殊字首,讓呼叫者指定必須建立和使用特定的 Resource
實現。
雖然 Resource
介面在 Spring 中被廣泛使用,但它本身作為一個通用工具類在您的程式碼中訪問資源也非常方便,即使您的程式碼不知道或不關心 Spring 的任何其他部分。雖然這會將您的程式碼與 Spring 耦合,但它實際上只與這小組工具類耦合,這些工具類可以作為 URL
的更強大的替代品,可以被認為是您用於此目的的任何其他庫的等價物。
Resource 抽象不會替換功能。它在可能的情況下進行包裝。例如,UrlResource 包裝一個 URL,並使用被包裝的 URL 來完成其工作。 |
內建的 Resource
實現
Spring 包含幾種內建的 Resource
實現
有關 Spring 中所有可用的 Resource
實現的完整列表,請查閱 Resource
的 Javadoc 文件中的“所有已知實現類”部分。
UrlResource
UrlResource
包裝了 java.net.URL
,可用於訪問通常可以透過 URL 訪問的任何物件,例如檔案、HTTPS 目標、FTP 目標等。所有 URL 都有標準化的 String
表示形式,以便使用適當的標準化字首來區分不同的 URL 型別。這包括用於訪問檔案系統路徑的 file:
,透過 HTTPS 協議訪問資源的 https:
,透過 FTP 訪問資源的 ftp:
等。
UrlResource
是透過 Java 程式碼顯式使用 UrlResource
建構函式建立的,但在呼叫接受表示路徑的 String
引數的 API 方法時,通常是隱式建立的。在後一種情況下,JavaBeans PropertyEditor
最終決定建立哪種型別的 Resource
。如果路徑字串包含一個眾所周知(對屬性編輯器而言)的字首(例如 classpath:
),它會為該字首建立一個適當的專用 Resource
。但是,如果它不識別該字首,它會假定該字串是一個標準的 URL 字串,並建立一個 UrlResource
。
ClassPathResource
此類表示應從類路徑獲取的資源。它使用執行緒上下文類載入器、給定的類載入器或給定的類來載入資源。
如果類路徑資源位於檔案系統中,則此 Resource
實現支援解析為 java.io.File
,但對於駐留在 jar 中且未被(由 servlet 引擎或任何環境)展開到檔案系統的類路徑資源則不支援。為了解決此問題,各種 Resource
實現始終支援解析為 java.net.URL
。
ClassPathResource
是透過 Java 程式碼顯式使用 ClassPathResource
建構函式建立的,但在呼叫接受表示路徑的 String
引數的 API 方法時,通常是隱式建立的。在後一種情況下,JavaBeans PropertyEditor
會識別字符串路徑上的特殊字首 classpath:
,並在該情況下建立一個 ClassPathResource
。
FileSystemResource
這是一個用於 java.io.File
控制代碼的 Resource
實現。它還支援 java.nio.file.Path
控制代碼,應用 Spring 標準的基於 String 的路徑轉換,但所有操作都透過 java.nio.file.Files
API 執行。對於純粹基於 java.nio.path.Path
的支援,請改用 PathResource
。FileSystemResource
支援解析為 File
和 URL
。
PathResource
這是一個用於 java.nio.file.Path
控制代碼的 Resource
實現,所有操作和轉換都透過 Path
API 執行。它支援解析為 File
和 URL
,並且還實現了擴充套件的 WritableResource
介面。PathResource
實際上是 FileSystemResource
的一個純粹基於 java.nio.path.Path
的替代品,其 createRelative
行為不同。
ServletContextResource
這是一個用於 ServletContext
資源的 Resource
實現,它解釋相關 Web 應用根目錄中的相對路徑。
它始終支援流訪問和 URL 訪問,但只有當 Web 應用歸檔檔案被解壓且資源實際位於檔案系統上時,才允許 java.io.File
訪問。它是否被解壓並在檔案系統上,或者直接從 JAR 或其他地方(例如資料庫,這是可以想象的)訪問,實際上取決於 Servlet 容器。
ResourceLoader
介面
ResourceLoader
介面旨在由可以返回(即載入)Resource
例項的物件實現。以下列表顯示了 ResourceLoader
介面的定義
public interface ResourceLoader {
Resource getResource(String location);
ClassLoader getClassLoader();
}
所有應用上下文都實現了 ResourceLoader
介面。因此,所有應用上下文都可以用於獲取 Resource
例項。
當您在特定的應用上下文上呼叫 getResource()
,並且指定的 location path 沒有特定的字首時,您將獲得一個適合該特定應用上下文的 Resource
型別。例如,假設以下程式碼片段針對 ClassPathXmlApplicationContext
例項執行
-
Java
-
Kotlin
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
val template = ctx.getResource("some/resource/path/myTemplate.txt")
對於 ClassPathXmlApplicationContext
,該程式碼返回一個 ClassPathResource
。如果對 FileSystemXmlApplicationContext
例項執行相同的方法,它將返回一個 FileSystemResource
。對於 WebApplicationContext
,它將返回一個 ServletContextResource
。它會類似地為每個上下文返回適當的物件。
因此,您可以以適合特定應用上下文的方式載入資源。
另一方面,您也可以透過指定特殊的 classpath:
字首來強制使用 ClassPathResource
,無論應用上下文型別如何,如下例所示
-
Java
-
Kotlin
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
val template = ctx.getResource("classpath:some/resource/path/myTemplate.txt")
類似地,您可以透過指定任何標準的 java.net.URL
字首來強制使用 UrlResource
。以下示例使用 file
和 https
字首
-
Java
-
Kotlin
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
val template = ctx.getResource("file:///some/resource/path/myTemplate.txt")
-
Java
-
Kotlin
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
val template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt")
下表總結了將 String
物件轉換為 Resource
物件的策略
字首 | 示例 | 說明 |
---|---|---|
classpath |
|
從類路徑載入。 |
file |
|
從檔案系統載入為 |
https |
|
載入為 |
(無) |
|
取決於底層的 |
ResourcePatternResolver
介面
ResourcePatternResolver
介面是 ResourceLoader
介面的擴充套件,它定義了一種策略,用於將位置模式(例如,Ant 風格路徑模式)解析為 Resource
物件。
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException;
}
如上所示,此介面還為類路徑中的所有匹配資源定義了一個特殊的 classpath*:
資源字首。請注意,在這種情況下,資源位置應該是一個沒有佔位符的路徑——例如,classpath*:/config/beans.xml
。類路徑中的 JAR 檔案或不同目錄可以包含具有相同路徑和相同名稱的多個檔案。有關使用 classpath*:
資源字首的萬用字元支援的更多詳細資訊,請參見 應用上下文建構函式資源路徑中的萬用字元 及其子節。
可以檢查傳入的 ResourceLoader
(例如,透過 ResourceLoaderAware
語義提供的載入器)是否也實現了此擴充套件介面。
PathMatchingResourcePatternResolver
是一個獨立的實現,可在 ApplicationContext
外部使用,並且還被 ResourceArrayPropertyEditor
用於填充 Resource[]
Bean 屬性。PathMatchingResourcePatternResolver
能夠將指定的資源位置路徑解析為一個或多個匹配的 Resource
物件。源路徑可以是與目標 Resource
具有一對一對映的簡單路徑,也可以包含特殊的 classpath*:
字首和/或內部的 Ant 風格正則表示式(使用 Spring 的 org.springframework.util.AntPathMatcher
工具進行匹配)。後兩者實際上都是萬用字元。
任何標準 |
ResourceLoaderAware
介面
ResourceLoaderAware
介面是一個特殊的 callback 介面,它標識那些期望獲得 ResourceLoader
引用的元件。以下清單顯示了 ResourceLoaderAware
介面的定義
public interface ResourceLoaderAware {
void setResourceLoader(ResourceLoader resourceLoader);
}
當一個類實現 ResourceLoaderAware
並被部署到應用上下文(作為 Spring 管理的 Bean)中時,應用上下文會將其識別為 ResourceLoaderAware
。然後應用上下文會呼叫 setResourceLoader(ResourceLoader)
方法,將自身作為引數提供(記住,Spring 中的所有應用上下文都實現了 ResourceLoader
介面)。
由於 ApplicationContext
也是一個 ResourceLoader
,因此 Bean 也可以實現 ApplicationContextAware
介面並直接使用提供的應用上下文來載入資源。然而,通常情況下,如果只需要資源載入功能,最好使用專門的 ResourceLoader
介面。這樣程式碼只會耦合到資源載入介面(可以被視為一個工具介面),而不會耦合到整個 Spring ApplicationContext
介面。
在應用元件中,你也可以依靠 ResourceLoader
的自動注入作為實現 ResourceLoaderAware
介面的替代方案。傳統的 constructor
和 byType
自動注入模式(如 自動注入協作者 中所述)能夠分別為建構函式引數或 setter 方法引數提供 ResourceLoader
。為了獲得更大的靈活性(包括自動注入欄位和多引數方法的能力),請考慮使用基於註解的自動注入特性。在這種情況下,只要相關的欄位、建構函式或方法帶有 @Autowired
註解,ResourceLoader
就會被自動注入到期望 ResourceLoader
型別的欄位、建構函式引數或方法引數中。更多資訊請參見 使用 @Autowired
。
對於包含萬用字元或使用特殊 classpath*: 資源字首的資源路徑,如果需要載入一個或多個 Resource 物件,可以考慮將 ResourcePatternResolver 的例項自動注入到你的應用元件中,而不是 ResourceLoader 。 |
作為依賴的資源
如果 Bean 本身要透過某種動態過程來確定和提供資源路徑,那麼 Bean 使用 ResourceLoader
或 ResourcePatternResolver
介面來載入資源可能是合理的。例如,考慮載入某種模板,其中所需的特定資源取決於使用者的角色。如果資源是靜態的,那麼完全取消使用 ResourceLoader
介面(或 ResourcePatternResolver
介面)是合理的,讓 Bean 暴露其所需的 Resource
屬性,並期望它們被注入進來。
使得這些屬性的注入變得簡單的是,所有應用上下文都註冊並使用一個特殊的 JavaBeans PropertyEditor
(屬性編輯器),它可以將 String
路徑轉換為 Resource
物件。例如,以下 MyBean
類有一個型別為 Resource
的 template
屬性。
-
Java
-
Kotlin
public class MyBean {
private Resource template;
public setTemplate(Resource template) {
this.template = template;
}
// ...
}
class MyBean(var template: Resource)
在 XML 配置檔案中,template
屬性可以使用該資源的簡單字串進行配置,如下例所示:
<bean id="myBean" class="example.MyBean">
<property name="template" value="some/resource/path/myTemplate.txt"/>
</bean>
請注意,資源路徑沒有字首。因此,由於應用上下文字身將被用作 ResourceLoader
,資源將透過 ClassPathResource
、FileSystemResource
或 ServletContextResource
進行載入,具體取決於應用上下文的確切型別。
如果你需要強制使用特定的 Resource
型別,可以使用字首。以下兩個示例展示瞭如何強制使用 ClassPathResource
和 UrlResource
(後者用於訪問檔案系統中的檔案):
<property name="template" value="classpath:some/resource/path/myTemplate.txt">
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>
如果將 MyBean
類重構為與註解驅動的配置一起使用,則 myTemplate.txt
的路徑可以儲存在名為 template.path
的鍵下——例如,在提供給 Spring Environment
的屬性檔案中(參見 環境抽象)。然後可以透過 @Value
註解使用屬性佔位符來引用模板路徑(參見 使用 @Value
)。Spring 將檢索模板路徑的值作為字串,並且一個特殊的 PropertyEditor
(屬性編輯器)將把該字串轉換為 Resource
物件,注入到 MyBean
的建構函式中。以下示例展示瞭如何實現這一點。
-
Java
-
Kotlin
@Component
public class MyBean {
private final Resource template;
public MyBean(@Value("${template.path}") Resource template) {
this.template = template;
}
// ...
}
@Component
class MyBean(@Value("\${template.path}") private val template: Resource)
如果我們想支援在類路徑中多個位置(例如,類路徑中的多個 jar 包)的同一路徑下發現多個模板,我們可以使用特殊的 classpath*:
字首和萬用字元來將 templates.path
鍵定義為 classpath*:/config/templates/*.txt
。如果我們按照如下方式重新定義 MyBean
類,Spring 會將模板路徑模式轉換為 Resource
物件的陣列,該陣列可以注入到 MyBean
的建構函式中。
-
Java
-
Kotlin
@Component
public class MyBean {
private final Resource[] templates;
public MyBean(@Value("${templates.path}") Resource[] templates) {
this.templates = templates;
}
// ...
}
@Component
class MyBean(@Value("\${templates.path}") private val templates: Resource[])
應用上下文和資源路徑
本節介紹如何使用資源建立應用上下文,包括適用於 XML 的快捷方式、如何使用萬用字元以及其他詳細資訊。
構造應用上下文
應用上下文建構函式(針對特定的應用上下文型別)通常接受一個字串或字串陣列作為資源的路徑位置,例如構成上下文定義的 XML 檔案。
當此類位置路徑沒有字首時,根據該路徑構建並用於載入 Bean 定義的特定 Resource
型別取決於具體的應用上下文,並且與該上下文相適應。例如,考慮以下示例,它建立了一個 ClassPathXmlApplicationContext
:
-
Java
-
Kotlin
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
val ctx = ClassPathXmlApplicationContext("conf/appContext.xml")
Bean 定義從類路徑載入,因為使用了 ClassPathResource
。然而,考慮以下示例,它建立了一個 FileSystemXmlApplicationContext
:
-
Java
-
Kotlin
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/appContext.xml");
val ctx = FileSystemXmlApplicationContext("conf/appContext.xml")
現在 Bean 定義從檔案系統位置載入(在這種情況下,相對於當前工作目錄)。
請注意,在位置路徑上使用特殊的 classpath
字首或標準 URL 字首會覆蓋用於載入 Bean 定義的預設 Resource
型別。考慮以下示例:
-
Java
-
Kotlin
ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
val ctx = FileSystemXmlApplicationContext("classpath:conf/appContext.xml")
使用 FileSystemXmlApplicationContext
從類路徑載入 Bean 定義。然而,它仍然是一個 FileSystemXmlApplicationContext
。如果隨後將其用作 ResourceLoader
,任何沒有字首的路徑仍將被視為檔案系統路徑。
構造 ClassPathXmlApplicationContext
例項 — 快捷方式
ClassPathXmlApplicationContext
公開了多個建構函式以方便例項化。基本思想是,你只需提供一個只包含 XML 檔名本身(不帶前導路徑資訊)的字串陣列,並提供一個 Class
。然後 ClassPathXmlApplicationContext
會從提供的類派生路徑資訊。
考慮以下目錄佈局:
com/ example/ services.xml repositories.xml MessengerService.class
以下示例展示瞭如何例項化一個由名為 services.xml
和 repositories.xml
檔案(它們位於類路徑中)中定義的 Bean 組成的 ClassPathXmlApplicationContext
例項:
-
Java
-
Kotlin
ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "repositories.xml"}, MessengerService.class);
val ctx = ClassPathXmlApplicationContext(arrayOf("services.xml", "repositories.xml"), MessengerService::class.java)
有關各種建構函式的詳細資訊,請參閱 ClassPathXmlApplicationContext
的 javadoc。
應用上下文建構函式資源路徑中的萬用字元
應用上下文建構函式值中的資源路徑可以是簡單的路徑(如前所示),每個路徑與目標 Resource
具有一對一的對映,或者,可以包含特殊的 classpath*:
字首或內部的 Ant 風格模式(透過使用 Spring 的 PathMatcher
工具進行匹配)。後兩者實際上都是萬用字元。
這種機制的一個用途是當你需要進行元件式應用程式組裝時。所有元件都可以將上下文定義片段釋出到已知的位置路徑,並且當使用帶有 classpath*:
字首的相同路徑建立最終的應用上下文時,所有元件片段都會自動被拾取。
請注意,這種萬用字元使用僅限於應用上下文建構函式中的資源路徑(或者當你直接使用 PathMatcher
工具類層次結構時),並在構造時解析。它與 Resource
型別本身無關。你不能使用 classpath*:
字首來構造一個實際的 Resource
,因為一個資源一次只指向一個資源。
Ant 風格模式
路徑位置可以包含 Ant 風格模式,如下例所示:
/WEB-INF/*-context.xml com/mycompany/**/applicationContext.xml file:C:/some/path/*-context.xml classpath:com/mycompany/**/applicationContext.xml
當路徑位置包含 Ant 風格模式時,解析器會遵循一個更復雜的程式來嘗試解析萬用字元。它會為直到最後一個非萬用字元段的路徑生成一個 Resource
並從中獲取一個 URL。如果此 URL 不是 jar:
URL 或容器特定的變體(例如 WebLogic 中的 zip:
,WebSphere 中的 wsjar
等),則會從中獲取 java.io.File
並透過遍歷檔案系統來解析萬用字元。對於 jar URL,解析器要麼從中獲取 java.net.JarURLConnection
,要麼手動解析 jar URL,然後遍歷 jar 檔案的內容來解析萬用字元。
對可移植性的影響
如果指定的路徑已經是 file
URL(無論是隱式因為基礎 ResourceLoader
是檔案系統型別的,還是顯式指定),萬用字元的使用可以保證以完全可移植的方式工作。
如果指定的路徑是 classpath
位置,解析器必須透過呼叫 Classloader.getResource()
來獲取最後一個非萬用字元路徑段的 URL。由於這只是路徑的一個節點(而不是末尾的檔案),在這種情況下返回的 URL 型別實際上是未定義的(在 ClassLoader
的 javadoc 中)。實際上,它總是代表目錄的 java.io.File
(當類路徑資源解析到檔案系統位置時)或某種 jar URL(當類路徑資源解析到 jar 位置時)。儘管如此,此操作仍存在可移植性問題。
如果最後一個非萬用字元段獲得了 jar URL,解析器必須能夠從中獲取 java.net.JarURLConnection
或手動解析 jar URL,以便遍歷 jar 的內容並解析萬用字元。這在大多數環境中有效,但在其他環境中會失敗,我們強烈建議在使用來自 jar 的資源的萬用字元解析之前,在你的特定環境中進行徹底測試。
classpath*:
字首
構造基於 XML 的應用上下文時,位置字串可以使用特殊的 classpath*:
字首,如下例所示:
-
Java
-
Kotlin
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
val ctx = ClassPathXmlApplicationContext("classpath*:conf/appContext.xml")
這個特殊字首指定必須獲取所有匹配給定名稱的類路徑資源(在內部,這主要透過呼叫 ClassLoader.getResources(…)
來實現),然後合併它們以形成最終的應用上下文定義。
萬用字元類路徑依賴於底層 ClassLoader 的 getResources() 方法。由於如今大多數應用伺服器提供了自己的 ClassLoader 實現,行為可能會有所不同,尤其是在處理 jar 檔案時。一個簡單的測試來檢查 classpath* 是否工作是使用 ClassLoader 從類路徑中的 jar 包內載入檔案:getClass().getClassLoader().getResources("<someFileInsideTheJar>") 。用具有相同名稱但位於兩個不同位置的檔案進行此測試——例如,在類路徑中的不同 jar 包內具有相同名稱和相同路徑的檔案。如果返回了不恰當的結果,請查閱應用伺服器文件中可能影響 ClassLoader 行為的設定。 |
你也可以將 classpath*:
字首與位置路徑的其餘部分中的 PathMatcher
模式結合使用(例如,classpath*:META-INF/*-beans.xml
)。在這種情況下,解析策略非常簡單:對最後一個非萬用字元路徑段使用 ClassLoader.getResources()
呼叫,以獲取類載入器層次結構中的所有匹配資源,然後,對每個資源,使用前面描述的相同 PathMatcher
解析策略來處理萬用字元子路徑。
與萬用字元相關的其他注意事項
請注意,classpath*:
與 Ant 風格模式結合使用時,除非實際目標檔案位於檔案系統中,否則只有在模式開始之前至少有一個根目錄時才能可靠工作。這意味著 classpath*:*.xml
這樣的模式可能無法從 jar 檔案的根目錄中檢索檔案,而只能從展開目錄的根目錄中檢索檔案。
Spring 檢索類路徑條目的能力源於 JDK 的 ClassLoader.getResources()
方法,該方法只為(表示潛在搜尋根的)空字串返回檔案系統位置。Spring 還會評估 URLClassLoader
執行時配置和 jar 檔案中的 java.class.path
manifest,但這並不能保證帶來可移植的行為。
掃描類路徑包需要類路徑中存在相應的目錄條目。使用 Ant 構建 JAR 檔案時,請勿啟用 JAR 任務的 在模組路徑(Java Module System)上,Spring 的類路徑掃描通常按預期工作。同樣強烈建議將資源放入專用目錄中,這樣可以避免前面提到的搜尋 jar 檔案根級別時的可移植性問題。 |
如果如果要搜尋的根包在多個類路徑位置可用,帶有 classpath:
資源的 Ant 風格模式不保證能找到匹配的資源。考慮以下資源位置示例:
com/mycompany/package1/service-context.xml
現在考慮某人可能用來嘗試查詢該檔案的 Ant 風格路徑:
classpath:com/mycompany/**/service-context.xml
此類資源可能僅存在於類路徑中的一個位置,但是當使用上述示例中的路徑嘗試解析它時,解析器會基於 getResource("com/mycompany");
返回的(第一個)URL 進行工作。如果此基本包節點存在於多個 ClassLoader
位置,則所需資源可能不存在於找到的第一個位置中。因此,在這種情況下,你應該優先使用帶有相同 Ant 風格模式的 classpath*:
,它會搜尋所有包含 com.mycompany
基本包的類路徑位置:classpath*:com/mycompany/**/service-context.xml
。
FileSystemResource
注意事項
未附加到 FileSystemApplicationContext
的 FileSystemResource
(即 FileSystemApplicationContext
不是實際的 ResourceLoader
時),會如你預期地處理絕對路徑和相對路徑。相對路徑相對於當前工作目錄,而絕對路徑相對於檔案系統的根目錄。
然而,出於向後相容性(歷史)原因,當 FileSystemApplicationContext
是 ResourceLoader
時,情況發生了變化。FileSystemApplicationContext
會強制所有附加的 FileSystemResource
例項將所有位置路徑都視為相對路徑,無論它們是否以斜槓開頭。實際上,這意味著以下示例是等效的:
-
Java
-
Kotlin
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/context.xml");
val ctx = FileSystemXmlApplicationContext("conf/context.xml")
-
Java
-
Kotlin
ApplicationContext ctx =
new FileSystemXmlApplicationContext("/conf/context.xml");
val ctx = FileSystemXmlApplicationContext("/conf/context.xml")
以下示例也是等效的(儘管從語義上講它們應該不同,因為一個情況是相對路徑而另一個是絕對路徑):
-
Java
-
Kotlin
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt");
val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("some/resource/path/myTemplate.txt")
-
Java
-
Kotlin
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt");
val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("/some/resource/path/myTemplate.txt")
實際上,如果你需要真正的絕對檔案系統路徑,你應該避免使用 FileSystemResource
或 FileSystemXmlApplicationContext
的絕對路徑,並透過使用 file:
URL 字首來強制使用 UrlResource
。以下示例展示瞭如何這樣做:
-
Java
-
Kotlin
// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt");
// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt")
-
Java
-
Kotlin
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
ApplicationContext ctx =
new FileSystemXmlApplicationContext("file:///conf/context.xml");
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
val ctx = FileSystemXmlApplicationContext("file:///conf/context.xml")