javascript
Spring系列13:Resource接口及内置实现
本文內(nèi)容
一、Resource接口的定義
Java 的標準 java.net.URL 類和各種 URL 前綴的標準處理程序不足以滿足所有對低級資源的訪問。 例如沒有標準化的 URL 實現(xiàn)可用于訪問需要從類路徑或相對于 ServletContext 獲取的資源。 雖然可以為專門的 URL 前綴注冊新的處理程序(類似于現(xiàn)有的前綴處理程序如 http:),但這通常相當復雜,并且 URL 接口仍然缺乏一些理想的功能,例如檢查是否存在的方法 指向的資源。
針對上述這種情況,Spring 提供了更強大的接口Resource用于對低級資源的抽象訪問,其定義和主要接口方法說明如下。
package org.springframework.core.io;public interface Resource extends InputStreamSource {// 特定資源是否存在boolean exists();// 內(nèi)容非空可通過#getInputStream()讀取default boolean isReadable() {return exists();}// 資源對應的InputStream是否已經(jīng)打開,不能多次讀取,應讀取后關閉避免資源泄漏default boolean isOpen() {return false;}// 是否是 File 類型,配合 #getFile()default boolean isFile() {return false;}// 獲取 URLURL getURL() throws IOException;// 獲取URIURI getURI() throws IOException;// 獲取 FileFile getFile() throws IOException;// 資源內(nèi)容長度long contentLength() throws IOException;// 上次修改的時間戳long lastModified() throws IOException;// 給定路徑創(chuàng)建資源Resource createRelative(String relativePath) throws IOException;// 獲取文件名非全路徑@NullableString getFilename();// 獲取資源描述String getDescription();}Resource接口繼承了InputStreamSource,其定義如下。
package org.springframework.core.io;public interface InputStreamSource {// 獲取資源對應的 InputStreamInputStream getInputStream() throws IOException; }Resource接口并不是用于完全取代java.net.URL,而是盡可能地通過其實現(xiàn)類來包裝URL進行處理,如UrlResource 包裝一個 URL 并使用包裝的 URL 來完成它的的功能。具體看下一節(jié)的內(nèi)置實現(xiàn)。
二、Resource接口的內(nèi)置實現(xiàn)
- UrlResource
- ClassPathResource
- FileSystemResource
- ServletContextResource
- InputStreamResource
- ByteArrayResource
2.1、UrlResource
UrlResource 包裝了 java.net.URL,可用于訪問通常可通過 URL 訪問的任何對象,例如文件、HTTP 目標、FTP 目標等。這些資源通過標準前綴來區(qū)分,file:用于訪問文件系統(tǒng)路徑,http:用于通過 HTTP 協(xié)議訪問資源,ftp:用于通過 FTP 訪問資源等。
2.2、ClassPathResource
ClassPathResource表示應從類路徑獲取的資源。它使用線程上下文類加載器、給定的類加載器或給定的類來加載資源。可通過特定前綴classpath:指定為該資源。
如果類路徑資源駐留在文件系統(tǒng)中,則此 Resource 實現(xiàn)支持解析為 java.io.File,但不支持在 jar 包中且尚未解壓的類路徑資源。
2.3、FileSystemResource
這是 java.io.File 和 java.nio.file.Path 句柄的資源實現(xiàn)。它支持作為File和 URL 的解析。
2.4、ServletContextResource
這是 ServletContext 資源的 Resource 實現(xiàn),它解釋相關 Web 應用程序根目錄中的相對路徑。
它始終支持流訪問和 URL 訪問,但僅在擴展 Web 應用程序存檔并且資源物理位于文件系統(tǒng)上時才允許 java.io.File 訪問。它是否被解壓并在文件系統(tǒng)上或直接從 JAR 或其他地方(如數(shù)據(jù)庫)訪問實際上取決于 Servlet 容器。
2.5、ByteArrayResource
給定字節(jié)數(shù)組的資源實現(xiàn)。它為給定的字節(jié)數(shù)組創(chuàng)建一個 ByteArrayInputStream。對于從任何給定的字節(jié)數(shù)組加載內(nèi)容很有用,而不必求助于單一使用的 InputStreamResource。
2.6、InputStreamResource
InputStreamResource 是給定 InputStream 的資源實現(xiàn)。首選 ByteArrayResource 或任何基于文件的 Resource 實現(xiàn),僅當沒有特定的 Resource 實現(xiàn)適用時才應使用它。
與其他 Resource 實現(xiàn)相比,這是一個已打開資源的描述符,接口isOpen()返回true。如果需要將資源描述符保存在某處或需要多次讀取流,請不要使用它。
2.7、ResourceLoader接口
ResourceLoader 用于加載資源(例如類路徑或文件系統(tǒng)資源)的策略接口。
package org.springframework.core.io;public interface ResourceLoader {/** Pseudo URL prefix for loading from the class path: "classpath:". */String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;// 返回指定資源位置的資源句柄Resource getResource(String location);// 獲取 ClassLoader// 需要直接訪問 ClassLoader 的客戶端可以通過 ResourceLoader 統(tǒng)一的方式進行訪問,而不是依賴于線程上 下文 ClassLoader。@NullableClassLoader getClassLoader();}源碼中對于的說明:
1、句柄應始終是可重用的資源描述符,允許多個 Resource.getInputStream() 調(diào)用。
2、必須支持完全限定的 URL,例如“file:C:/test.dat”。
3、必須支持類路徑偽 URL,例如“classpath:test.dat”。應該支持相對文件路徑,例如“WEB-INF/test.dat”。 (這將是特定于實現(xiàn)的,通常由 ApplicationContext 實現(xiàn)提供。)
4、資源句柄并不意味著現(xiàn)有資源;需要調(diào)用 Resource.exists() 來檢查是否存在。
所有org.springframework.context.ApplicationContext都必須實現(xiàn)該ResourceLoader接口。調(diào)用getResource()接口的返回值類型取決于當前context的類型,當前context會自動轉換。如下案例。
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");如ctx是ClassPathXmlApplicationContext返回ClassPathResource;
如ctx是FileSystemXmlApplicationContext返回FileSystemResource;
如ctx是WebApplicationContext返回 ServletContextResource
如果不想依賴context類型決定返回資源的類型,可以指定前綴的方式,強制返回特定類型的資源。如下案例。
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt"); // 返回 ClassPathResource Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt"); Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt"); // 返回 UrlResource指定前綴對于資源加載方式和返回的影響對應關系如下表。
| classpath: | classpath:com/myapp/config.xml | 從類路徑加載 |
| file: | `file:///data/config.xml | 從文件系統(tǒng)作為URL 加載 |
| http: | https://myserver/logo.png | 從文件系統(tǒng)作為 URL 加載 |
| (none) | /data/config.xml | 取決于底層的 ApplicationContext |
ResourceLoaderAware 接口
ResourceLoaderAware 接口是一個特殊的回調(diào)接口,它標識期望提供 ResourceLoader 引用的組件。其定義如下。
由于 org.springframework.context.ApplicationContext實現(xiàn)了 ResourceLoader,因此 bean 還可以實現(xiàn) ApplicationContextAware 接口并直接使用提供的應用程序上下文來加載資源。從面向接口編程和解耦的角度來說,需要ResourceLoader的來進行資源加載,更推薦實現(xiàn)``ResourceLoaderAware接口。
深一層思考,如果容器中的一個bean需要一個ResourceLoader依賴用于加載資源,除了實現(xiàn)ResourceLoaderAware,是否還有其它方式呢?
ResourceLoader實例會自動注入到IoC容器,我們可通過構造函數(shù)、setter方法、@Autowire注解等方式,直接注入該依賴,具體看一看之前Ioc相關的文章。
配置文件當做Resource 注入到bean依賴中
如果某個bean依賴于特定的Resource,那么我們?nèi)绾慰焖賰?yōu)雅地注入該依賴呢?直接上代碼。
MyConfig依賴一個類文件路徑下的一個配置文件。
對應的xml文件spring.xml和配置文db.properties件如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="myConfig" class="com.crab.spring.ioc.demo02.MyConfig"><property name="template" value="db.properties"></property></bean> </beans> url=jdbc:mysql://localhost/test username=root password=456 max=1024運行主程序如下:
package com.crab.spring.ioc.demo02;import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource;public class ResourceTest {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring" +".xml");MyConfig myConfig = context.getBean(MyConfig.class);Resource template = myConfig.getTemplate();System.out.println(template);System.out.println(template instanceof ClassPathResource);} }運行結果:
class path resource [db.properties] true class path resource [db.properties]- 成功注入了Resourece資源到MyConfig的bean中
- 注入Resource實際是我們通過ClassPathXmlApplicationContext加載的ClassPathResource
擴展:結合上一篇的資源特定前綴和ApplicationContext的關系,忘了請翻看下,我們也可以指定前綴來加載特定的資源。較為簡單就不演示了。
<bean id="myConfig" class="com.crab.spring.ioc.demo02.MyConfig"><property name="template" value="classpath:db.properties"></property></bean>file:///some/resource/path <bean id="myConfig" class="com.crab.spring.ioc.demo02.MyConfig"><property name="template" value="file:///some/resource/path"></property></bean> // 注入的是FileSystemResource三、使用資源路徑配置應用上下文
回顧一下,ResourceLoader章節(jié),我們分析了指定資源路徑前綴對于資源加載方式的影響,對應關系如下表。下面將分帶前綴和不帶前綴的方式來配置應用上下文。
| classpath: | classpath:com/myapp/config.xml | 從類路徑加載 |
| file: | `file:///data/config.xml | 從文件系統(tǒng)作為 URL 加載 |
| http: | https://myserver/logo.png | 從文件系統(tǒng)作為 URL 加載 |
| (none) | /data/config.xml | 取決于底層的 ApplicationContext |
資源路徑不帶前綴
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");上面這個案例資源路徑不帶前綴,ClassPathXmlApplicationContext將使用ClassPathResource加載在類路徑下的資源文件。再來一個案例。
ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");FileSystemXmlApplicationContext將使用URLResouce來加載文件系統(tǒng)中的配置文件,這里案例是相對路徑。
資源路徑帶前綴
當指定了資源前綴,則使用指定前綴對應的Resource來加載資源。如下案例。
ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");對比上一個案例,此處將使用ClassPathResource加載在類路徑下的資源文件。
資源路徑帶前綴和通配符
指定單一的資源文件通過前面2種方式,若需要指定多個資源則可以考慮使用通配符,支持Ant-style和classpath*。
1、Ant-style通配符
/WEB-INF/*-context.xml com/mycompany/**/applicationContext.xml file:C:/some/path/*-context.xml classpath:com/mycompany/**/applicationContext.xml當使用Ant-style通配符,解析器遵循更復雜的過程來嘗試解析通配符。它為直到最后一個非通配符段的路徑生成一個資源,并從中獲取一個 URL。它為直到最后一個非通配符段的路徑生成一個資源,并從中獲取一個 URL。
最后一個非通配符段的路徑分2種情況處理:
- 普通的文件路徑:
生成java.io.File,如classpath:com/mycompany/**/applicationContext.xml取到classpath:com/mycompany,然后進行遍歷。 - 特殊的文件jar:
路徑:如classpath:com/mycompany/**/applicationContext.xml,取到classpath:com/mycompany
,將生成 java.net.JarURLConnection,或是手動解析 jar URL然后遍歷 jar 文件的內(nèi)容來解析通配符。
使用 jar URL強烈建議測試下是否能正常通過通配符訪問到資源
2、classpath*前綴
如下案例
ApplicationContext ctx =new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");遍歷類路徑下所有匹配到conf/appContext.xml的資源都會加載。
3、兩種方式可以組合使用
如classpath*:META-INF/*-beans.xml
解決策略相當簡單:在最后一個非通配符路徑段上使用 ClassLoader.getResources() 調(diào)用來獲取類加載器層次結構中的所有匹配資源,然后對于每個資源,PathMatcher 解析策略用于通配符子路徑。感興趣的可以了解下PathMatcher 類。
組合使用容易掉坑的2個地方,請注意:
-
classpath*😗.xml
可能不會從 類路徑下的jar 文件的根目錄中檢索文件,而只能從擴展目錄的根目錄中檢索文件。
-
classpath:com/mycompany/**/service-context.xml
如果要搜索的根包在多個類路徑位置中可用,則不保證資源能夠找到匹配的資源。 使用ClassLoader#getResource(com/mycompany/)如果多個路徑有,則可能只返回第一個,其它的漏掉了。保險的做法是classpath*:com/mycompany/**/service-context.xml
彩蛋
出于向后兼容的歷史原因, FileSystemXmlApplicationContext關聯(lián)的FileSystemResource會將所有的資源的非前綴路徑統(tǒng)一當做相對路徑。上案例
總結
本文主要詳細說明了
這一篇下來,基本Spring關于資源處理和相關接口總體是比較清晰了。
總結
以上是生活随笔為你收集整理的Spring系列13:Resource接口及内置实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 风变编程学习笔记for循环应用:演员的作
- 下一篇: angular表格添加_AngularJ