Spring 5 中文解析核心篇-IoC容器之Resource

這個章節涵蓋了Spring怎樣處理和在Spring中使用資源文件。包括下面主題:html

2.1 介紹

Java的標準java.net.URL類和標準處理URL前綴變體,不幸地,對於全部訪問低級資源的能力還不夠。例如,這裏沒有須要從類路徑或相關聯的ServletContext獲取資源使用的標準URL實現。固然也能夠註冊新的處理器爲特定URL前綴(相似已經存在的前置處理器,例如:http:),一般這是十分的複雜,而且URL接口仍然缺少一些功能描述,例如一種檢查所指向資源是否存在的方法。java

2.2 Resource接口

Spring的Resource接口意思是爲抽象獲取低級別資源提供更多的能力。下面清單顯示了Resource接口定義:git

public interface Resource extends InputStreamSource {

    boolean exists();

    boolean isOpen();

    URL getURL() throws IOException;

    File getFile() throws IOException;

    Resource createRelative(String relativePath) throws IOException;

    String getFilename();

    String getDescription();
}

Resource接口定義顯示這樣,它拓展了InputStreamSource接口。下面清單顯示InputStreamSource接口的定義:正則表達式

public interface InputStreamSource {

    InputStream getInputStream() throws IOException;
}

一些Resource接口中重要的方法:spring

  • getInputStream():定位和打開資源而且返回從資源中讀取的InputStream。每次調用都返回一個新的InputStream。咱們有責任去關閉流。
  • exists():任何一個boolean指示是否這個資源在物理路徑存在。
  • isOpen():而後一個boolean指示此資源是否表明打開流的句柄。若是爲trueInputStream不能被屢次讀取且只能讀取一次,並且須要關閉資源避免被泄露。對於全部常規資源實現,返回false,但InputStreamResource除外。
  • getDescription():返回這個資源的描述,以在使用該資源時用於錯誤輸出。這一般是標準文件名或資源的實際URL。

其餘方法容許你獲取一個真實URL或FIle對象描述資源(若是底層實現是兼容和支持該功能)。數據庫

當須要資源時,Spring自身普遍地使用Resource抽象,在許多方法簽名上參數類型。一些Spring API中的其餘方法(例如,各類ApplicationContext實現的構造函數)採用String形式,該字符串以未經修飾或簡單的形式用於建立適合該上下文實現的Resource,或者經過String路徑上的特殊前綴,讓調用者指定必須建立並使用特定的資源實現。編程

雖然Resource接口在Spring中被大量使用,可是在你本身的代碼中做爲一個通用的實用程序類來使用它其實是很是有用的,以便訪問資源,即便你的代碼不知道或不關心Spring的任何其餘部分。雖然這個耦合Spring到你的代碼,它僅僅耦合很是小的工具類集合,能夠用做URL的更強大替代,而且能夠認爲與你將用於此目的的任何其餘庫等效。api

Resource抽象不能替換功能。它儘量地包裝它。例如, UrlResource包裝URL和使用包裝的URL去工做。
2.3 內建的Resource實現

Spring包含下面的Resource實現:數組

2.3.1 UrlResource

UrlResource包裝了java.net.URL,可用於訪問一般能夠經過URL訪問的任何對象,例如文件,HTTP目標、FTP目標等。全部URL有一個標準化的String表示,所以使用適當的標準化前綴來指示一種URL類型。這包括獲取文件系統路徑file:、經過HTTP協議獲取資源http:、經過FTP獲取資源ftp:等等。安全

UrlResource是由Java代碼經過顯式使用UrlResource構造函數建立的,但一般在調用帶有String參數表示路徑的API方法時隱式建立。對於後一種狀況,JavaBean PropertyEditor最終決定哪種Resource類型被建立。若是字符串包含咱們所知的前綴(例如 classpath:),它建立一個適當的指定前綴的Resource。然而,若是不能識別前綴,假設字符串是標準的URL字符串並建立一個UrlResource

2.3.2 ClassPathResource

這個類表明一個可以從類路徑獲取的資源。它使用線程上下文類加載器、給定的類加載器或給定的類來加載資源。

這個資源實現支持將解析做爲java.io.File。若是類路徑資源駐留在文件系統中,而不是類路徑資源駐留在jar中,而且沒有(由servlet引擎或其餘環境)擴展到文件系統。爲了解決這個問題,各類Resource實現始終支持將解析做爲java.net.URL進行。

當你採用一個String參數描述路徑調用API方法時,ClassPathResource是經過顯示使用ClassPathResource構造函數經過Java代碼建立,可是一般隱式地被建立。對於後一種狀況,javabean PropertyEditor識別在字符串路徑中指定前綴classpath:而且在這個場景中建立一個ClassPathResource

2.3.3 FileSystemResource

這是java.io.Filejava.nio.file.Path句柄的Resource實現。它支持解析做爲File和URL。

2.3.4 ServletContextResource

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

它始終支持流訪問和URL訪問,但僅在擴展Web應用程序實現被擴展和資源在實際文件系統上時才容許java.io.File訪問。它是在文件系統上擴展仍是直接擴展,或者直接從JAR或其餘相似數據庫(能夠想到的)中訪問,實際上取決於Servlet容器。

2.3.5 InputStreamResource

InputStreamResource是一個給定InputStreamResource實現。僅當沒有特定的資源實現時才應使用它。特別是,在可能的狀況下,最好選擇ByteArrayResource或任何基於文件的Resource實現。

對比其餘的Resource實現,這是一個對於已經打開的資源的描述。所以,isOpen()方法返回true。若是你須要將資源描述符保留在某個地方或須要屢次讀取流,請不要使用它。

2.3.6 ByteArrayResource

這是一個給定byte數組的Resource實現。它爲給定byte數組建立一個ByteArrayInputStream

這對於從任何給定的字節數組加載內容頗有用,而沒必要求助於一次性InputStreamResource

參考代碼: com.liyong.ioccontainer.starter.ResourceIocContainer
2.4 ResourceLoader

ResourceLoader接口是由能夠返回(即加載)資源實例的對象實現的。下面清單顯示ResourceLoader定義:

public interface ResourceLoader {

    Resource getResource(String location);
}

全部的應用上下文實現ResourceLoader接口。所以,全部應用上下文可使用去獲取Resource實例。

當你在指定應用上下文上調用getResource(),而且定義路徑沒有指定前綴,你能夠獲取特定應用上下文中適合的Resource類型。例如,假定針對ClassPathXmlApplicationContext實例執行了如下代碼片斷:

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

針對ClassPathXmlApplicationContext,這個代碼返回一個ClassPathResource實例。針對FileSystemXmlApplicationContext若是相同方法被執行,它將返回FileSystemResource。對於WebApplicationContext,它將返回一個ServletContextResource。它將相似地爲每個上下文返回適合的對象。

最後,你能夠以適合特定應用程序上下文的方式加載資源。

另外一方面,你也能夠強制使用ClassPathResource,無論應用上下文類型,經過指定classpath:前綴,相似下面例子顯示:

Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");

相似地,你能夠經過指定標準的java.net.URL前綴強制使用UrlResource。下面兩個例子使用filehttp前綴:

Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");

下面表格總結對於轉換String對象到Resource對象的策略 :

Prefix Example Explanation
classpath: classpath:com/myapp/config.xml 類路徑加載
file: file:///data/config.xml 做爲URL資源從文件系統加載 FileSystemResourceCaveats.
http: https://myserver/logo.png 做爲URL加載
(none) /data/config.xml 依賴底層ApplicationContext
2.5 ResourceLoaderAware接口

ResourceLoaderAware接口是一個特殊的回調接口,它表示但願使用ResourceLoader引用提供的組件。下面清單顯示ResourceLoaderAware接口定義:

public interface ResourceLoaderAware {

   void setResourceLoader(ResourceLoader resourceLoader);
}

當一個類實現ResourceLoaderAware而且被部署到應用上下文中(做爲Spring管理的bean),它應用上下文做爲ResourceLoaderAware被識別。應用上下文調用setResourceLoader(ResourceLoader),應用自身做爲參數(記住,Spring中的全部應用程序上下文都實現ResourceLoader接口)。

由於ApplicationContextResourceLoader,bean也能夠實現ApplicationContextAware接口而且使用使用應用上下文直接地加載資源。可是,一般,若是須要的話,最好使用專門的ResourceLoader接口。該代碼將僅耦合到資源加載接口(能夠視爲實用程序接口),而不耦合到整個Spring ApplicationContext接口。

在應用中的組件,你也能夠依賴注入ResourceLoader做爲實現ResourceLoaderAware接口替代方案。「傳統」構造函數和byType自動裝配模式(如「自動裝配協做器」中所述)可以分別爲構造函數參數或setter方法參數提供ResourceLoader。爲了更大的靈活性(包含自動裝配字段和多參數方法),考慮使用基於註解的特性。在這種狀況下,ResourceLoader自動裝配到字段、構造函數或方法參數、字段、構造函數或方法攜帶有@Autowired註解,就會指望ResourceLoader類型。更多消息,查看@Autowired使用。

2.6 依賴Resources

若是Bean自己將經過某種動態過程來肯定和提供資源路徑,那麼對於Bean來講,使用ResourceLoader接口加載資源多是有意義的。例如,考慮加載某種模板,其中所需的特定資源取決於用戶的角色。若是資源是靜態的,那麼徹底消除ResourceLoader接口的使用是有意義的,讓bean公開它須要的資源屬性,並指望將它們注入到bean中。

而後注入這些屬性的麻煩之處在於,全部應用程序上下文都註冊並使用了特殊的JavaBeans PropertyEditor,它能夠將String路徑轉換爲Resource對象。若是myBean有一個Resource類型模版屬性,它能夠爲資源配置一個簡單的字符串,相似下面例子顯示:

<bean id="myBean" class="...">
    <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"/>
參考代碼: com.liyong.ioccontainer.starter.Resource2IocContainer
2.7 應用上下文和資源路徑

這個章節涵蓋怎樣建立應用上下文與資源,包括與XML一塊兒使用的快捷方式,如何使用通配符以及其餘詳細信息

2.7.1 構造應用上下文

應用上下文構造一般地採用字符串或資源位置路徑字符串,例如構成上下文定義的XML文件。

當位置路徑沒有前綴時,從該路徑構建並用於加載Bean定義的特定Resource類型取決於特定應用程序上下文,而且適用於該特定應用程序上下文。例如,考慮下面例子,建立一個ClassPathXmlApplicationContext

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

bean定義從類路徑被加載,由於ClassPathResource被使用。然而,考慮下面例子,建立一個FileSystemXmlApplicationContext

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

bean定義從文件系統位置被加載(在這個場景中,相對於當前工做目錄)。

注意,使用位置路徑上的特殊類路徑前綴或標準URL前綴會覆蓋爲加載定義而建立的默認資源類型。

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

使用FileSystemXmlApplicationContext從類路徑加載bean定義。然而,它仍然是一個FileSystemXmlApplicationContext。若是隨後將其用做ResourceLoader,則任何無前綴的路徑仍將視爲文件系統路徑。

構造ClassPathXmlApplicationContext實例-快捷方式

ClassPathXmlApplicationContext暴露許多構造方法去實例化。基本思想是,你只提供一個字符串數組,該字符串數組僅包含XML文件本自身的文件名(不包含前導路徑信息),而且還提供一個Class。而後,ClassPathXmlApplicationContext從提供的類中獲取路徑信息。

考慮下面路徑佈局:

com/
  foo/
    services.xml
    daos.xml
    MessengerService.class

如下示例顯示如何實例化由在名爲service.xmldaos.xml(位於類路徑中)的文件中定義的bean組成的ClassPathXmlApplicationContext實例:

ApplicationContext ctx = new ClassPathXmlApplicationContext(
    new String[] {"services.xml", "daos.xml"}, MessengerService.class);

查看ClassPathXmlApplicationContext詳細javadock的各類構造。

2.7.2 應用程序上下文構造函數資源路徑中的通配符

應用程序上下文構造函數值中的資源路徑能夠是簡單路徑(如先前所示),每一個路徑都具備到目標資源的一對一映射,或者能夠包含特殊的「classpath*:」前綴或內部Ant常規樣式的正則表達式(經過使用Spring的PathMatcher進行匹配)。後者都是有效的通配符。

這種機制使用之一是當你須要組件風格封裝應用時。全部組件均可以將上下文定義片斷「發佈」到一個已知的位置路徑,而且,當使用以classpath*:做爲前綴的相同路徑建立最終的應用程序上下文時,全部組件片斷都會被自動獲取。

注意,這種通配符是特定於在應用程序上下文構造函數中使用資源路徑的(或者當你直接使用PathMatcher程序類層次結構時),並在構造時解析。它與資源類型自己無關。你不能使用classpath*:前綴來構造實際的資源,由於一個資源每次只指向一個資源。

Ant風格模式

路徑位置能夠包含Ant風格模式,相似下面例子顯示:

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

當路徑位置包含一個Ant風格模式時,解析器容許更復雜程序嘗試去解析通配符。它爲最後一個非通配符段的路徑生成一個資源,並從中得到一個URL。若是這個URL不是jar:URL或包含特定變體(例如,在WebLogic中的zip:、在WebSphere中的wsjar等等),從中得到一個java.io.File,並經過遍歷文件系統來解析通配符。對於jar URL,解析器能夠從中獲取java.net.Jar URLConnection,也能夠手動解析jar URL,而後遍歷jar文件的內容以解析通配符。

影響可移植性

若是指定的路徑已是一個文件URL(因爲基本ResourceLoader是一個文件系統,因此它是隱式的,或者是顯式的),則保證通配符能夠徹底可移植的方式工做。

若是指定的路徑是類路徑位置,則解析器必須經過調用Classloader.getResource()得到最後的非通配符路徑段URL。因爲這只是路徑的一個節點(而不是末尾的文件),所以實際上(在ClassLoader javadoc中)未定義確切返回的是哪一種URL。。在實踐中,它老是一個java.io.File描述目錄(類路徑資源解析爲文件系統的位置)或一個一些種類(類路徑資源解析爲jar位置)jar URL。儘管如此,此操做仍存在可移植性問題。

若是爲最後一個非通配符段獲取了jar URL,則解析器必須可以從中獲取java.net.Jar URLConnection或手動解析jar URL,以便可以遍歷jar的內容並解析通配符。這在大多數環境中確實有效,但在其餘環境中則無效,所以咱們強烈建議在依賴特定環境以前,對來自jars的資源的通配符解析進行完全測試。

classpath*:前綴

當構造基於XML應用上下文時,位置字符串可能使用指定的classpath*:前綴,相似下面例子顯示:

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

這個特殊的前綴指定必須獲取與給定名稱匹配的全部類路徑資源(內部,其實是經過調用ClassLoader.getResources(…)發生的),而後合併造成最終的應用程序上下文定義。

通配符類路徑依賴類加載器底層的 getResources()方法。因爲當今大多數應用程序服務器都提供本身的類加載器實現,所以行爲可能有所不一樣,尤爲是在處理jar文件時。檢查 classpath *是否有效的一個簡單測試是使用 classloaderclasspath的jar中加載文件:

getClass().getClassLoader().getResources("<someFileInsideTheJar>")。嘗試對具備相同名稱但位於兩個不一樣位置的文件進行此測試。若是返回了不合適的結果,請檢查應用程序服務器文檔中可能會影響類加載器行爲的設置。

你能夠在其他的位置路徑中將classpath*:前綴與PathMatcher模式結合使用(例如,classpath*:META-INF/*-beans.xml),在這個場景中,解析策略是至關地簡單:在最後一個非通配符路徑段上使用ClassLoader.getResources()調用,以獲取類加載器層次結構中的全部匹配資源,而後在每一個資源以外,對通配符子路徑使用前面所述的相同PathMatcher解析策略。

通配符相關的其餘注意

注意,當與ant樣式模式結合使用時,除非實際目標文件位於文件系統中,不然classpath*:只能在模式啓動以前可靠地與至少一個根目錄一塊兒工做。這意味着諸如classpath * : * .xml之類的模式可能不會從jar文件的根目錄檢索文件,而只會從擴展目錄的根目錄檢索文件。

Spring檢索類路徑條目的能力源於JDK的ClassLoader.getResources()方法,該方法返回文件系統中的空字符串位置(表示可能要搜索的根)。Spring還會評估jar文件中的URLClassLoader運行時配置和java.class.path清單,但這不能保證會致使可移植行爲。

類路徑包掃描要求在類路徑中對於目錄條目存在。使用Ant構建JAR時,請勿激活JAR任務的文件開關。另外,在某些環境中,基於安全策略,類路徑目錄可能不會公開。例如,JDK 1.7.0_45及更高版本上的獨立應用程序(這須要在清單中設置「受信任的庫」)查看 https://stackoverflow.com/que...

在JDK9的模塊路徑上,Spring的類路徑掃描一般能夠正常進行。強烈建議在此處將資源放入專用目錄,以免在搜索jar文件根目錄級別時出現上述可移植性問題。

具備classpath:的Ant樣式模式:若是要搜索的根包在多個類路徑位置可用,不能保證找到匹配的資源。考慮下面資源爲主例子:

com/mycompany/package1/service-context.xml

如今考慮一個ant樣式的路徑,有人可能會使用它來嘗試查找該文件Ant格式路徑

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

這些資源可能只在一個位置,可是當使用諸如前面示例的路徑嘗試對其進行解析時,解析器將處理getResource( "com/mycompany」)返回的(第一個)URL。若是這個基礎包節點在多個類加載器路徑中存在,實際的最終資源可能不存在。所以,在這種狀況下,你應該首選使用具備相同Ant樣式模式的classpath *:,該模式將搜索包含根包的全部類路徑位置。

2.7.3 FileSystemResource注意事項

未附加到FileSystemApplicationContextFileSystemResource(即,當FileSystemApplicationContext不是實際的ResourceLoader時)將按你指望的方式處理絕對路徑和相對路徑。相對路徑是相對於當前工做目錄的,而絕對路徑是相對於文件系統的根的。

可是,出於向後兼容性(歷史)的緣由,當FileSystemApplicationContextResourceLoader時,狀況會發生變化。FileSystemApplicationContext強制全部附加的FileSystemResource實例將全部位置路徑視爲相對路徑,無論它們是否以正斜槓開頭。在實踐中,這意味着如下示例是等效的:

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");

在實踐中,若是你須要文件系統絕對路徑,你須要避免將絕對路與FileSystemResourceFileSystemXmlApplicationContext一塊兒使用,而且強制經過使用file:前綴的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 its definition via a UrlResource
ApplicationContext ctx =
    new FileSystemXmlApplicationContext("file:///conf/context.xml");

做者

我的從事金融行業,就任過易極付、思建科技、某網約車平臺等重慶一流技術團隊,目前就任於某銀行負責統一支付系統建設。自身對金融行業有強烈的愛好。同時也實踐大數據、數據存儲、自動化集成和部署、分佈式微服務、響應式編程、人工智能等領域。同時也熱衷於技術分享創立公衆號和博客站點對知識體系進行分享。關注公衆號: 青年IT男 獲取最新技術文章推送!

博客地址: http://youngitman.tech

CSDN: https://blog.csdn.net/liyong1...

微信公衆號:

技術交流羣:

相關文章
相關標籤/搜索