1 資源抽象接口(Resource)
JDK 所提供的訪問資源的類(如 java.net.URL、File 等)并不能很好的滿足各種底層資源的訪問需求,比如缺少從類路徑或者 Web 容器的上下文中獲取資源的操作類 。 為此, Spring 設(shè)計(jì)了一個(gè) Resource 接口,它為應(yīng)用程序提供了更強(qiáng)的訪問底層資源的能力 。
Resource 接口的主要方法:
| 方法 | 說明 |
|---|---|
| boolean exists() | 是否存在 |
| boolean isOpen() | 是否打開 |
| URL getURL() throws IOException | 如果底層資源可以表示成 URL ,則返回對應(yīng)的 URL 對象 |
| File getFile() throws IOException | 如果底層資源對應(yīng)一個(gè)文件,則返回對應(yīng)的 File 對象 |
| InputStream getInputStream() throws IOException | 返回資源對應(yīng)的輸入流 |

| 接口或類 | 說明 |
|---|---|
| WritableResource | 可寫資源接口(Spring 3.1 + 新增),有兩個(gè)實(shí)現(xiàn)類:FileSystemResource 和 PathResource(Spring 4.0 + 新增) |
| ByteArrayResource | 二進(jìn)制數(shù)組表示的資源,二進(jìn)制數(shù)組資源可以在內(nèi)存中通過程序構(gòu)造。 |
| ClassPathResource | 類路徑下的資源,資源以相對于類路徑的方式表示。 |
| FileSystemResource | 文件系統(tǒng)資源,資源以文件系統(tǒng)路徑的方式表示,如 D:/config.xml。 |
| InputStreamResource | 以輸入流返回表示的資源。 |
| ServletContextResource | 以相對于 Web 應(yīng)用根目錄的路徑下加載資源。支持以流和 URL 的方式訪問資源;在 WAR 被解壓的情況下,也可以通過 File 方式訪問資源;還可以直接從 JAR 包中訪問資源。 |
| UrlResource | Url 封裝了 ava.net.URL,它能夠訪問如文件系統(tǒng)的資源、HTTP 以及 FTP 等資源。 |
| PathResource | Path 封裝了 java.net.URL、java.nio.file.Path(Java 7.0 +)和文件系統(tǒng)資源,通過它可以訪問 URL、Path 和系統(tǒng)文件路徑表示的資源。 |
有了這個(gè)抽象的資源類之后,就可以將 Spring 的配置信息放置在任意地方咯O(∩_∩)O哈哈~
Spring 的 Resource 接口及其實(shí)現(xiàn)類,可以在脫離 Spring 框架的情況下實(shí)現(xiàn),它與 JDK 提供的資源訪問方式相比,更強(qiáng)大,也更好用。
假設(shè)需要訪問 Web 應(yīng)用類路徑下的一個(gè)文件,那么我們可以使用以下這些方法:
- FileSystemResource - 以文件系統(tǒng)的絕對路徑的方式進(jìn)行訪問。
- ClassPathResource - 以類路徑的方式進(jìn)行訪問。
- ServletContextResource - 以相對于 Web 應(yīng)用根目錄的路徑的方式進(jìn)行訪問。
public class FileSourceTest {
public static void main(String[] args) throws IOException {
String filePath="F:\\temp\\config.xml";
//以系統(tǒng)文件路徑的方式加載資源
WritableResource writableResource=new PathResource(filePath);
//寫入
OutputStream outputStream=writableResource.getOutputStream();
outputStream.write("今天過得還好嗎?".getBytes());
outputStream.close();
//讀取
InputStream inputStream=writableResource.getInputStream();
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
int i;
while ((i=inputStream.read())!=-1){
byteArrayOutputStream.write(i);
}
System.out.println("byteArrayOutputStream:"+byteArrayOutputStream.toString());
System.out.println("writableResource-getFilename:"+writableResource.getFilename());
//使用類路徑的方式加載資源
Resource resource=new ClassPathResource("resources/spring-ioc.xml");
System.out.println("resource-getFilename:"+resource.getFilename());
}
}
Resource 接口定義一些方法,用于訪問文件的信息與數(shù)據(jù):
| 方法 | 說明 |
|---|---|
| getFileName() | 獲取文件名 |
| getFile() | 獲取資源對應(yīng)的 File 對象 |
| getInputStream() | 獲取文件的輸入流 |
WritableResource 接口定義一些方法,用于向文件寫入數(shù)據(jù):
| 方法 | 說明 |
|---|---|
| getOutputStream() | 獲取文件的輸出流 |
| createRelative(String relativePath) | 在相對地址上創(chuàng)建新文件 |
在 Web 應(yīng)用中,可以通過 ServletContextResource 以相對于 Web 應(yīng)用根目錄的路徑,加載文件資源:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8" %>
<jsp:directive.page import="org.springframework.web.context.support.ServletContextResource"/>
<jsp:directive.page import="org.springframework.core.io.Resource"/>
<jsp:directive.page import="org.springframework.web.util.WebUtils"/>
<%
Resource resource=new
ServletContextResource(application,"WEB-INF/resources/config.properties");//相對于 Web 應(yīng)用的根路徑
out.print(resource.getFilename()+"<br/>");
out.print(WebUtils.getTempDir(application).getAbsoluteFile());
%>
對于位于遠(yuǎn)程服務(wù)器(Web 服務(wù)器或 FTP 服務(wù)器)上的文件資源,可以使用 UrlResource 進(jìn)行訪問。
加載資源默認(rèn)采用系統(tǒng)編碼來讀取內(nèi)容。所以如果資源文件的內(nèi)容采用的是特殊的編碼格式,那么可以使用 EncodedResource 對資源進(jìn)行編碼:
EncodedResource encodedResource=new EncodedResource(resource,"UTF-8");
System.out.println(FileCopyUtils.copyToString(encodedResource.getReader()));
2 通過特殊標(biāo)識加載資源
Spring 提供了一個(gè)強(qiáng)大的加載資源的機(jī)制,可以通過 "classpath:"、"file:" 等資源地址前綴識別不同的資源類型,還支持 Ant 風(fēng)格的帶通配符的資源地址 。
2.1 資源地址表達(dá)式
| 地址前綴 | 說明 | 示例 |
|---|---|---|
| classpath: | 從類路徑中加載資源, classpath: 和 classpath:/ 是等價(jià)的,都是相對于類的根路徑 。 資源文件可以在標(biāo)準(zhǔn)文件系統(tǒng)中,也可以在 jar 或者 zip 文件中 。 | classpath:com/xxx/config.properties |
| file: | 使用 UrlResource 從文件系統(tǒng)目錄中裝載資源,可以采用絕對或者相對路徑 。 | file:com/xxx/config.properties |
| http:// | 使用 UrlResource 從 Web 服務(wù)器中裝載資源。 | http://www.xxx.com/xxx/config.properties |
| ftp:// | 使用 UrlResource 從 FTP 服務(wù)器中裝載資源。 | ftp://www.xxx.com/xxx/config.properties |
| 沒有前綴 | 根據(jù) ApplicationContext 具體實(shí)現(xiàn)類采用對應(yīng)類型的 Resource。 | com/xxx/config.properties |
"classpath*:" 前綴與 “classpath:” 前綴的區(qū)別如下:
假設(shè)有這樣的一個(gè)場景,假設(shè)有在 JAR 包或文件系統(tǒng)類路徑下,存在多個(gè)同名的包名(比如 com.deniro)——
- "classpath*:" 前綴 - 會掃描所有這些 JAR 包或文件系統(tǒng)類路徑下的同名包。
- “classpath:” 前綴 - 只會掃描這些 JAR 包或文件系統(tǒng)類路徑下的第一個(gè)被加載的包。
這種設(shè)計(jì)對于分模塊打包的應(yīng)用場景下很有用。假設(shè)一個(gè)名為 note 的應(yīng)用,分為 2 個(gè)模塊,每個(gè)模塊對應(yīng)一個(gè)配置文件(module1.xml 與 module2.xml ),都放置在 com.note 目錄下,每個(gè)模塊單獨(dú)打 JAR 包。使用 classpath*:com/note/module*.xml 就可以同時(shí)加載這些模塊中的配置文件啦O(∩_∩)O哈哈~
Ant 風(fēng)格的資源地址支持以下 3 種匹配符:
| 匹配符 | 說明 |
|---|---|
| ? | 匹配文件名中的一個(gè)字符。 |
| * | 匹配文件名中的任意個(gè)字符 。 |
| ** | 匹配多層路徑。 |
2.2 資源加載器
Spring 定義了加載資源的接口以及實(shí)現(xiàn)類:

| 接口或類 | 說明 |
|---|---|
| ResourceLoader | getResource(String location) 方法,可以根據(jù)地址加載資源,僅支持帶資源類型前綴的表達(dá)式。 |
| ResourcePatternResolver | 擴(kuò)展了 ResourceLoader 接口,定義的 getResources(String locationPattern) 方法,支持帶資源類型前綴及 Ant 風(fēng)格的資源路徑表達(dá)式。 |
| PathMatchingResourcePatternResolver | 是 ResourcePatternResolver 的實(shí)現(xiàn)類。 |
public void getResources() throws IOException {
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath*:net/deniro/**/*.class");
Assert.assertNotNull(resources);
for (Resource resource : resources) {
System.out.println(resource.getDescription());
}
}
在項(xiàng)目發(fā)布時(shí),如果資源配置文件會被打包到 JAR 中,如果使用 Resource#getFile() 方法,會拋出 FileNotFoundException,我們可以使用 Resource#getInputStream() 來讀取它。建議在實(shí)踐中,盡量采用流的方式來讀取配置文件,因?yàn)樗偸怯行У呐?O(∩_∩)O哈哈~