說說在 Spring 框架中如何訪問資源(文件、URL、二進(jìn)制數(shù)組等)

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)的輸入流
Resource 接口及實(shí)現(xiàn)類之間的關(guān)系
接口或類 說明
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哈哈~


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容