資源

簡介

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。

其他方法可讓您取得代表資源的實際 URLFile 物件 (如果底層實作相容且支援該功能)。

Resource 介面的一些實作也實作了擴展的 WritableResource 介面,用於支援寫入的資源。

Spring 本身廣泛使用 Resource 抽象化,在許多方法簽名中,當需要資源時作為引數類型。某些 Spring API 中的其他方法 (例如各種 ApplicationContext 實作的建構子) 接受 String,未經修飾或簡單的形式用於建立適合該 Context 實作的 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

此類別表示應從類別路徑取得的資源。它使用執行緒 Context 類別載入器、給定的類別載入器或給定的類別來載入資源。

如果類別路徑資源位於檔案系統中,則此 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 的支援,請改用 PathResourceFileSystemResource 支援解析為 FileURL

PathResource

這是用於 java.nio.file.Path 句柄的 Resource 實作,透過 Path API 執行所有操作和轉換。它支援解析為 FileURL,並且還實作了擴展的 WritableResource 介面。PathResource 實際上是 FileSystemResource 的純粹基於 java.nio.path.Path 的替代方案,具有不同的 createRelative 行為。

ServletContextResource

這是用於 ServletContext 資源的 Resource 實作,它解釋相關 Web 應用程式根目錄中的相對路徑。

它始終支援串流存取和 URL 存取,但僅當 Web 應用程式歸檔檔已擴展且資源實際上位於檔案系統上時,才允許 java.io.File 存取。它是否已擴展並位於檔案系統上,還是直接從 JAR 或其他位置 (例如資料庫,這是可以想像的) 存取,實際上取決於 Servlet 容器。

InputStreamResource

InputStreamResource 是給定 InputStreamResource 實作。僅當沒有適用的特定 Resource 實作時才應使用它。特別是,盡可能優先選擇 ByteArrayResource 或任何基於檔案的 Resource 實作。

與其他 Resource 實作相反,這是對已開啟資源的描述符。因此,它從 isOpen() 傳回 true。如果您需要將資源描述符保留在某處,或者如果您需要多次讀取串流,請勿使用它。

ByteArrayResource

這是給定位元組陣列的 Resource 實作。它為給定的位元組陣列建立一個 ByteArrayInputStream

它適用於從任何給定的位元組陣列載入內容,而無需求助於單次使用的 InputStreamResource

ResourceLoader 介面

ResourceLoader 介面旨在由可以傳回 (即載入) Resource 實例的物件實作。以下清單顯示了 ResourceLoader 介面定義:

public interface ResourceLoader {

	Resource getResource(String location);

	ClassLoader getClassLoader();
}

所有應用程式 Context 都實作了 ResourceLoader 介面。因此,所有應用程式 Context 都可以用於取得 Resource 實例。

當您在特定的應用程式 Context 上呼叫 getResource(),並且指定的location路徑沒有特定的前綴時,您會取回一個適合該特定應用程式 Context 的 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。對於每個 Context,它都會類似地傳回適當的物件。

因此,您可以以適合特定應用程式 Context 的方式載入資源。

另一方面,您也可以強制使用 ClassPathResource,無論應用程式 Context 類型如何,方法是指定特殊前綴 classpath:,如下例所示:

  • 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。以下範例使用 filehttps 前綴:

  • 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 物件的策略:

表 1. 資源字串
前綴 範例 說明

classpath

classpath:com/myapp/config.xml

從類別路徑載入。

file

file:///data/config.xml

作為 URL 從檔案系統載入。另請參閱 FileSystemResource 注意事項

https

https://myserver/logo.png

作為 URL 載入。

(無)

/data/config.xml

取決於底層 ApplicationContext

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*: 資源前綴的萬用字元支援的更多詳細資訊,請參閱 應用程式 Context 建構子資源路徑中的萬用字元 及其子章節。

可以檢查傳入的 ResourceLoader (例如,透過 ResourceLoaderAware 語意提供的資源載入器) 是否也實作了此擴展介面。

PathMatchingResourcePatternResolver 是一個獨立的實作,可在 ApplicationContext 之外使用,並且也由 ResourceArrayPropertyEditor 用於填充 Resource[] Bean 屬性。PathMatchingResourcePatternResolver 能夠將指定資源位置路徑解析為一個或多個匹配的 Resource 物件。來源路徑可以是與目標 Resource 具有一對一映射的簡單路徑,或者可以包含特殊的 classpath*: 前綴和/或內部 Ant 樣式的正則運算式 (使用 Spring 的 org.springframework.util.AntPathMatcher 工具程式進行匹配)。後兩者實際上都是萬用字元。

在任何標準 ApplicationContext 中的預設 ResourceLoader 實際上是 PathMatchingResourcePatternResolver 的實例,它實作了 ResourcePatternResolver 介面。對於 ApplicationContext 實例本身也是如此,它也實作了 ResourcePatternResolver 介面並委派給預設的 PathMatchingResourcePatternResolver

ResourceLoaderAware 介面

ResourceLoaderAware 介面是一個特殊的回呼介面,用於識別期望提供 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 介面的替代方案。傳統constructorbyType 自動裝配模式(如自動裝配協作者中所述)能夠為建構子參數或 setter 方法參數分別提供 ResourceLoader。為了獲得更大的彈性(包括自動裝配欄位和多個參數方法的能力),請考慮使用基於註解的自動裝配功能。在這種情況下,ResourceLoader 會自動裝配到期望 ResourceLoader 類型的欄位、建構子參數或方法參數中,只要相關的欄位、建構子或方法帶有 @Autowired 註解即可。有關更多資訊,請參閱使用 @Autowired

若要為包含萬用字元或使用特殊 classpath*: 資源前綴的資源路徑載入一個或多個 Resource 物件,請考慮將 ResourcePatternResolver 的實例自動裝配到您的應用程式組件中,而不是 ResourceLoader

資源作為依賴項

如果 bean 本身將通過某種動態過程來確定和提供資源路徑,那麼 bean 使用 ResourceLoaderResourcePatternResolver 介面來載入資源可能是有意義的。例如,考慮載入某種類型的範本,其中所需的特定資源取決於使用者的角色。如果資源是靜態的,那麼完全消除 ResourceLoader 介面(或 ResourcePatternResolver 介面)的使用,讓 bean 公開它需要的 Resource 屬性,並期望將它們注入到其中是有意義的。

使注入這些屬性變得簡單的是,所有應用程式上下文都註冊並使用一個特殊的 JavaBeans PropertyEditor,它可以將 String 路徑轉換為 Resource 物件。例如,以下 MyBean 類別具有一個 template 屬性,其類型為 Resource

  • 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,因此資源將通過 ClassPathResourceFileSystemResourceServletContextResource 載入,具體取決於應用程式上下文的確切類型。

如果您需要強制使用特定的 Resource 類型,可以使用前綴。以下兩個範例顯示如何強制使用 ClassPathResourceUrlResource(後者用於存取檔案系統中的檔案)

<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 檔案中)的相同路徑下發現的多個範本 — 例如,在類別路徑中的多個 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.xmlrepositories.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(…​) 來實現的),然後合併以形成最終的應用程式上下文定義。

萬用字元類別路徑依賴於底層 ClassLoadergetResources() 方法。由於現在大多數應用程式伺服器都提供自己的 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 任務的 files-only 開關。此外,基於某些環境中的安全策略,類別路徑目錄可能不會公開 — 例如,JDK 1.7.0_45 及更高版本上的獨立應用程式(這需要在您的資訊清單中設定 'Trusted-Library'。請參閱stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources)。

在模組路徑(Java 模組系統)上,Spring 的類別路徑掃描通常按預期工作。此處也強烈建議將資源放入專用目錄中,從而避免搜尋 jar 檔案根目錄級別時出現的上述可移植性問題。

如果要在多個類別路徑位置中找到要搜尋的根套件,則帶有 classpath: 資源的 Ant 樣式模式不能保證找到匹配的資源。考慮以下資源位置範例

com/mycompany/package1/service-context.xml

現在考慮某人可能用來嘗試查找該檔案的 Ant 樣式路徑

classpath:com/mycompany/**/service-context.xml

這樣的資源可能僅存在於類別路徑中的一個位置,但是當使用諸如前面範例的路徑來嘗試解析它時,解析器會根據 getResource("com/mycompany"); 返回的(第一個)URL 進行操作。如果此基本套件節點存在於多個 ClassLoader 位置中,則所需的資源可能不存在於找到的第一個位置中。因此,在這種情況下,您應該首選將 classpath*: 與相同的 Ant 樣式模式一起使用,這將搜尋包含 com.mycompany 基本套件的所有類別路徑位置:classpath*:com/mycompany/**/service-context.xml

FileSystemResource 注意事項

未附加到 FileSystemApplicationContextFileSystemResource(也就是說,當 FileSystemApplicationContext 不是實際的 ResourceLoader 時)會按照您的預期處理絕對路徑和相對路徑。相對路徑相對於當前工作目錄,而絕對路徑相對於檔案系統的根目錄。

但是,出於向後兼容性(歷史原因),當 FileSystemApplicationContextResourceLoader 時,情況會發生變化。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")

實際上,如果您需要真正的絕對檔案系統路徑,則應避免將絕對路徑與 FileSystemResourceFileSystemXmlApplicationContext 一起使用,並通過使用 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")