資源
本章涵蓋 Spring 如何處理資源以及如何在 Spring 中使用資源。它包括以下主題:
簡介
Java 的標準 java.net.URL 類以及各種 URL 字首的標準處理程式,不幸的是,對於所有對底層資源的訪問來說,都不是很足夠。例如,沒有標準化的 URL 實現可用於訪問需要從類路徑或相對於 ServletContext 獲取的資源。雖然可以為專用的 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 的任何其他部分。雖然這會將您的程式碼與 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 的標準基於字串的路徑轉換,但透過 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(),並且指定的位置路徑沒有特定字首時,您會獲得一個適合該特定應用程式上下文的 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*: 資源字首萬用字元支援的更多詳細資訊,請參閱 ApplicationContext 建構函式資源路徑中的萬用字元及其子部分。
傳入的 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。
Application Context 建構函式資源路徑中的萬用字元
應用程式上下文建構函式值中的資源路徑可以是簡單路徑(如前所示),每個路徑與目標 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 清單,但這並不能保證帶來可移植的行為。
|
類路徑包的掃描需要類路徑中存在相應的目錄條目。使用 Ant 構建 JAR 時,請不要啟用 JAR 任務的 在模組路徑(Java 模組系統)上,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 一起使用,並強制使用 UrlResource,方法是使用 file: URL 字首。以下示例顯示瞭如何執行此操作:
-
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")