本文內容
Resource介面的定義Resource介面的內置實作ResourceLoader介面ResourceLoaderAware介面
Resource介面的定義
Java 的標準 java.net.URL 類和各種 URL 前綴的標準處理程式不足以滿足所有對低級資源的訪問, 例如沒有標準化的 URL 實作可用于訪問需要從類路徑或相對于 ServletContext 獲取的資源, 雖然可以為專門的 URL 前綴注冊新的處理程式(類似于現有的前綴處理程式如 http:),但這通常相當復雜,并且 URL 介面仍然缺乏一些理想的功能,例如檢查是否存在的方法 指向的資源,
針對上述這種情況,Spring 提供了更強大的介面Resource用于對低級資源的抽象訪問,其定義和主要介面方法說明如下,
package org.springframework.core.io;
public interface Resource extends InputStreamSource {
// 特定資源是否存在
boolean exists();
// 內容非空可通過#getInputStream()讀取
default boolean isReadable() {
return exists();
}
// 資源對應的InputStream是否已經打開,不能多次讀取,應讀取后關閉避免資源泄漏
default boolean isOpen() {
return false;
}
// 是否是 File 型別,配合 #getFile()
default boolean isFile() {
return false;
}
// 獲取 URL
URL getURL() throws IOException;
// 獲取URI
URI getURI() throws IOException;
// 獲取 File
File getFile() throws IOException;
// 資源內容長度
long contentLength() throws IOException;
// 上次修改的時間戳
long lastModified() throws IOException;
// 給定路徑創建資源
Resource createRelative(String relativePath) throws IOException;
// 獲取檔案名非全路徑
@Nullable
String getFilename();
// 獲取資源描述
String getDescription();
}
Resource介面繼承了InputStreamSource,其定義如下,
package org.springframework.core.io;
public interface InputStreamSource {
// 獲取資源對應的 InputStream
InputStream getInputStream() throws IOException;
}
Resource介面并不是用于完全取代java.net.URL,而是盡可能地通過其實作類來包裝URL進行處理,如UrlResource 包裝一個 URL 并使用包裝的 URL 來完成它的的功能,具體看下一節的內置實作,
Resource介面的內置實作
UrlResourceClassPathResourceFileSystemResourceServletContextResourceInputStreamResourceByteArrayResource
UrlResource
UrlResource 包裝了 java.net.URL,可用于訪問通常可通過 URL 訪問的任何物件,例如檔案、HTTP 目標、FTP 目標等,這些資源通過標準前綴來區分,file:用于訪問檔案系統路徑,http:用于通過 HTTP 協議訪問資源,ftp:用于通過 FTP 訪問資源等,
ClassPathResource
ClassPathResource表示應從類路徑獲取的資源,它使用執行緒背景關系類加載器、給定的類加載器或給定的類來加載資源,可通過特定前綴classpath:指定為該資源,
如果類路徑資源駐留在檔案系統中,則此 Resource 實作支持決議為 java.io.File,但不支持在 jar 包中且尚未解壓的類路徑資源,
FileSystemResource
這是 java.io.File 和 java.nio.file.Path 句柄的資源實作,它支持作為File和 URL 的決議,
ServletContextResource
這是 ServletContext 資源的 Resource 實作,它解釋相關 Web 應用程式根目錄中的相對路徑,
它始終支持流訪問和 URL 訪問,但僅在擴展 Web 應用程式存檔并且資源物理位于檔案系統上時才允許 java.io.File 訪問,它是否被解壓并在檔案系統上或直接從 JAR 或其他地方(如資料庫)訪問實際上取決于 Servlet 容器,
ByteArrayResource
給定位元組陣列的資源實作,它為給定的位元組陣列創建一個 ByteArrayInputStream,對于從任何給定的位元組陣列加載內容很有用,而不必求助于單一使用的 InputStreamResource,
InputStreamResource
InputStreamResource 是給定 InputStream 的資源實作,首選 ByteArrayResource 或任何基于檔案的 Resource 實作,僅當沒有特定的 Resource 實作適用時才應使用它,
與其他 Resource 實作相比,這是一個已打開資源的描述符,介面isOpen()回傳true,如果需要將資源描述符保存在某處或需要多次讀取流,請不要使用它,
ResourceLoader介面
ResourceLoader 用于加載資源(例如類路徑或檔案系統資源)的策略介面,
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 統一的方式進行訪問,而不是依賴于執行緒上 下文 ClassLoader,
@Nullable
ClassLoader getClassLoader();
}
原始碼中對于的說明:
- 句柄應始終是可重用的資源描述符,允許多個 Resource.getInputStream() 呼叫,
- 必須支持完全限定的 URL,例如“
file:C:/test.dat”,- 必須支持類路徑偽 URL,例如“
classpath:test.dat”,- 應該支持相對檔案路徑,例如“
WEB-INF/test.dat”, (這將是特定于實作的,通常由ApplicationContext實作提供,)- 資源句柄并不意味著現有資源;需要呼叫
Resource.exists()來檢查是否存在,
所有 org.springframework.context.ApplicationContext都必須實作該ResourceLoader介面,呼叫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 | 從檔案系統作為 URL 加載 |
| http: | https://myserver/logo.png |
從檔案系統作為 URL 加載 |
| (none) | /data/config.xml |
取決于底層的 ApplicationContext |
ResourceLoaderAware 介面
ResourceLoaderAware 介面是一個特殊的回呼介面,它標識期望提供 ResourceLoader 參考的組件,其定義如下,
package org.springframework.context;
public interface ResourceLoaderAware extends Aware {
// 可自定義保存一個ResourceLoader的參考來進行資源加載
void setResourceLoader(ResourceLoader resourceLoader);
}
由于 org.springframework.context.ApplicationContext實作了 ResourceLoader,因此 bean 還可以實作 ApplicationContextAware 介面并直接使用提供的應用程式背景關系來加載資源,從面向介面編程和解耦的角度來說,需要ResourceLoader的來進行資源加載,更推薦實作``ResourceLoaderAware 介面,
深一層思考,如果容器中的一個bean需要一個ResourceLoader依賴用于加載資源,除了實作ResourceLoaderAware,是否還有其它方式呢?
ResourceLoader實體會自動注入到IoC容器,我們可通過建構式、setter方法、@Autowire注解等方式,直接注入該依賴,具體看一看之前Ioc相關的文章,
組態檔當做Resource 注入到bean依賴中
如果某個bean依賴于特定的Resource,那么我們如何快速優雅地注入該依賴呢?直接上代碼,
public class MyConfig {
/**
* 通過組態檔注入的資源
*/
private Resource template;
public Resource getTemplate() {
return template;
}
public void setTemplate(Resource template) {
this.template = templa
}
}
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" >
<property name="template" value="https://www.cnblogs.com/kongbubihai/p/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" >
<property name="template" value="https://www.cnblogs.com/kongbubihai/p/classpath:db.properties"></property>
</bean>file:///some/resource/path
<bean id="myConfig" >
<property name="template" value="file:///some/resource/path"></property>
</bean>
// 注入的是FileSystemResource
使用資源路徑配置應用背景關系
回顧一下,ResourceLoader章節,我們分析了指定資源路徑前綴對于資源加載方式的影響,對應關系如下表,下面將分帶前綴和不帶前綴的方式來配置應用背景關系,
| 前綴 | 例子 | 如何決議 |
|---|---|---|
| classpath: | classpath:com/myapp/config.xml |
從類路徑加載 |
| file: | `file:///data/config.xml | 從檔案系統作為 URL 加載 |
| http: | https://myserver/logo.png |
從檔案系統作為 URL 加載 |
| (none) | /data/config.xml |
取決于底層的 ApplicationContext |
資源路徑不帶前綴
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
上面這個案例資源路徑不帶前綴,ClassPathXmlApplicationContext將使用ClassPathResource加載在類路徑下的資源檔案,再來一個案例,
ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");
FileSystemXmlApplicationContext將使用URLResouce來加載檔案系統中的組態檔,這里案例是相對路徑,
資源路徑帶前綴
當指定了資源前綴,則使用指定前綴對應的Resource來加載資源,如下案例,
ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
對比上一個案例,此處將使用ClassPathResource加載在類路徑下的資源檔案,
資源路徑帶前綴和通配符
指定單一的資源檔案通過前面2種方式,若需要指定多個資源則可以考慮使用通配符,支持Ant-style和classpath*,
-
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,或是手動決議 jarURL然后遍歷 jar 檔案的內容來決議通配符,使用 jar
URL強烈建議測驗下是否能正常通過通配符訪問到資源
-
-
classpath*前綴如下案例
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");遍歷類路徑下所有匹配到
conf/appContext.xml的資源都會加載, -
兩種方式可以組合使用
如
classpath*:META-INF/*-beans.xml解決策略相當簡單:在最后一個非通配符路徑段上使用 ClassLoader.getResources() 呼叫來獲取類加載器層次結構中的所有匹配資源,然后對于每個資源,
PathMatcher決議策略用于通配符子路徑,感興趣的可以了解下PathMatcher類,組合使用容易掉坑的2個地方,請注意:
-
classpath*:*.xml可能不會從 類路徑下的jar 檔案的根目錄中檢索檔案,而只能從擴展目錄的根目錄中檢索檔案,
-
classpath:com/mycompany/**/service-context.xml如果要搜索的根包在多個類路徑位置中可用,則不保證資源能夠找到匹配的資源, 使用
ClassLoader#getResource(com/mycompany/)如果多個路徑有,則可能只回傳第一個,其它的漏掉了,保險的做法是classpath*:com/mycompany/**/service-context.xml
-
彩蛋
出于向后兼容的歷史原因, FileSystemXmlApplicationContext關聯的FileSystemResource會將所有的資源的非前綴路徑統一當做相對路徑,上案例
// 2種寫法是一樣的
ApplicationContext ctx =new FileSystemXmlApplicationContext("conf/context.xml");
ApplicationContext ctx =new FileSystemXmlApplicationContext("/conf/context.xml");
// 2種寫法是一樣的
ctx.getResource("some/resource/path/myTemplate.txt");
ctx.getResource("/some/resource/path/myTemplate.txt");
// 可以強制不按相對路徑處理不,可以的!按URLResource處理
ctx = new FileSystemXmlApplicationContext("file:///conf/context.xml");
總結
本文主要詳細說明了
Resource介面的定義和方法說明Resource介面的內置6種實作ResourceLoader介面和實作ResourceLoaderAware介面使用- 如何使用
Resource配置ApplicationContext
這一篇下來,基本Spring關于資源處理和相關介面總體是比較清晰了,
知識分享,轉載請注明出處,學無先后,達者為先!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/429239.html
標籤:Java
下一篇:Spring AOP
