Spring中Resource接口詳解

原本地址:https://www.cnblogs.com/yw0219/p/7255522.html
在日常程序開發中,處理外部資源是很繁瑣的事情,我們可能需要處理URL資源、File資源資源、ClassPath相關資源、服務器相關資源(JBoss AS 5.x上的VFS資源)等等很多資源。因此處理這些資源需要使用不同的接口,這就增加了我們系統的複雜性;而且處理這些資源步驟都是類似的(打開資源、讀取資源、關閉資源),因此如果能抽象出一個統一的接口來對這些底層資源進行統一訪問,是不是很方便,而且使我們系統更加簡潔,都是對不同的底層資源使用同一個接口進行訪問。
Spring 提供一個Resource接口來統一這些底層資源一致的訪問,而且提供了一些便利的接口,從而能提供我們的生產力。
4.1.2 Resource接口
Spring的Resource接口代表底層外部資源,提供了對底層外部資源的一致性訪問接口。

Java代碼

public interface InputStreamSource { 
InputStream getInputStream() throws IOException; 
} 

Java代碼

public interface Resource extends InputStreamSource { 
boolean exists(); 
boolean isReadable(); 
boolean isOpen(); 
URL getURL() throws IOException; 
URI getURI() throws IOException; 
File getFile() throws IOException; 
long contentLength() throws IOException; 
long lastModified() throws IOException; 
Resource createRelative(String relativePath) throws IOException; 
String getFilename(); 
String getDescription(); 
}

1)InputStreamSource接口解析:
getInputStream:每次調用都將返回一個新鮮的資源對應的java.io. InputStream字節流,調用者在使用完畢後必須關閉該資源。
2)Resource接口繼承InputStreamSource接口,並提供一些便利方法:
exists:返回當前Resource代表的底層資源是否存在,true表示存在。
isReadable:返回當前Resource代表的底層資源是否可讀,true表示可讀。
isOpen:返回當前Resource代表的底層資源是否已經打開,如果返回true,則只能被讀取一次然後關閉以避免資源泄露;常見的Resource實現一般返回false。
getURL:如果當前Resource代表的底層資源能由java.util.URL代表,則返回該URL,否則拋出IOException。
getURI:如果當前Resource代表的底層資源能由java.util.URI代表,則返回該URI,否則拋出IOException。
getFile:如果當前Resource代表的底層資源能由java.io.File代表,則返回該File,否則拋出IOException。
contentLength:返回當前Resource代表的底層文件資源的長度,一般是值代表的文件資源的長度。
lastModified:返回當前Resource代表的底層資源的最後修改時間。
createRelative:用於創建相對於當前Resource代表的底層資源的資源,比如當前Resource代表文件資源“d:/test/”則createRelative(“test.txt”)將返回表文件資源“d:/test/test.txt”Resource資源。
getFilename:返回當前Resource代表的底層文件資源的文件路徑,比如File資源“file://d:/test.txt”將返回“d:/test.txt”,而URL資源http://www.javass.cn將返回“”,因爲只返回文件路徑。
getDescription:返回當前Resource代表的底層資源的描述符,通常就是資源的全路徑(實際文件名或實際URL地址)。

Resource接口提供了足夠的抽象,足夠滿足我們日常使用。而且提供了很多內置Resource實現:ByteArrayResource、InputStreamResource 、FileSystemResource 、UrlResource 、ClassPathResource、ServletContextResource、VfsResource等。
4.2 內置Resource實現

4.2.1 ByteArrayResource
ByteArrayResource代表byte[]數組資源,對於“getInputStream”操作將返回一個ByteArrayInputStream。
首先讓我們看下使用ByteArrayResource如何處理byte數組資源:

Java代碼 收藏代碼

package cn.javass.spring.chapter4; 
import java.io.IOException; 
import java.io.InputStream; 
import org.junit.Test; 
import org.springframework.core.io.ByteArrayResource; 
import org.springframework.core.io.Resource; 
public class ResourceTest { 
@Test 
public void testByteArrayResource() { 
Resource resource = new ByteArrayResource("Hello World!".getBytes()); 
if(resource.exists()) { 
dumpStream(resource); 
} 
} 
} 

是不是很簡單,讓我們看下“dumpStream”實現:

Java代碼

private void dumpStream(Resource resource) { 
InputStream is = null; 
try { 
//1.獲取文件資源 
is = resource.getInputStream(); 
//2.讀取資源 
byte[] descBytes = new byte[is.available()]; 
is.read(descBytes); 
System.out.println(new String(descBytes)); 
} catch (IOException e) { 
e.printStackTrace(); 
} 
finally { 
try { 
//3.關閉資源 
is.close(); 
} catch (IOException e) { 
} 
} 
}

讓我們來仔細看一下代碼,dumpStream方法很抽象定義了訪問流的三部曲:打開資源、讀取資源、關閉資源,所以dunpStrean可以再進行抽象從而能在自己項目中使用;byteArrayResourceTest測試方法,也定義了基本步驟:定義資源、驗證資源存在、訪問資源。

ByteArrayResource可多次讀取數組資源,即isOpen ()永遠返回false。

1.2.2 InputStreamResource
InputStreamResource代表java.io.InputStream字節流,對於“getInputStream ”操作將直接返回該字節流,因此只能讀取一次該字節流,即“isOpen”永遠返回true。
讓我們看下測試代碼吧:

Java代碼

@Test 
public void testInputStreamResource() { 
ByteArrayInputStream bis = new ByteArrayInputStream("Hello World!".getBytes()); 
Resource resource = new InputStreamResource(bis); 
if(resource.exists()) { 
dumpStream(resource); 
} 
Assert.assertEquals(true, resource.isOpen()); 
} 

測試代碼幾乎和ByteArrayResource測試完全一樣,注意“isOpen”此處用於返回true。

4.2.3 FileSystemResource
FileSystemResource代表java.io.File資源,對於“getInputStream ”操作將返回底層文件的字節流,“isOpen”將永遠返回false,從而表示可多次讀取底層文件的字節流。
讓我們看下測試代碼吧:

Java代碼

@Test 
public void testFileResource() { 
File file = new File("d:/test.txt"); 
Resource resource = new FileSystemResource(file); 
if(resource.exists()) { 
dumpStream(resource); 
} 
Assert.assertEquals(false, resource.isOpen()); 
} 

注意由於“isOpen”將永遠返回false,所以可以多次調用dumpStream(resource)。

4.2.4 ClassPathResource
ClassPathResource代表classpath路徑的資源,將使用ClassLoader進行加載資源。classpath 資源存在於類路徑中的文件系統中或jar包裏,且“isOpen”永遠返回false,表示可多次讀取資源。
ClassPathResource加載資源替代了Class類和ClassLoader類的“getResource(String name)”和“getResourceAsStream(String name)”兩個加載類路徑資源方法,提供一致的訪問方式。
ClassPathResource提供了三個構造器:
public ClassPathResource(String path):使用默認的ClassLoader加載“path”類路徑資源;
public ClassPathResource(String path, ClassLoader classLoader):使用指定的ClassLoader加載“path”類路徑資源;
比如當前類路徑是“cn.javass.spring.chapter4.ResourceTest”,而需要加載的資源路徑是“cn/javass/spring/chapter4/test1.properties”,則將加載的資源在“cn/javass/spring/chapter4/test1.properties”;
public ClassPathResource(String path, Class

@Test 
public void testClasspathResourceByDefaultClassLoader() throws IOException { 
Resource resource = new ClassPathResource("cn/javass/spring/chapter4/test1.properties"); 
if(resource.exists()) { 
dumpStream(resource); 
} 
System.out.println("path:" + resource.getFile().getAbsolutePath()); 
Assert.assertEquals(false, resource.isOpen()); 
} 

2)使用指定的ClassLoader進行加載資源,將加載指定的ClassLoader類路徑上相對於根路徑的資源:

Java代碼

@Test 
public void testClasspathResourceByClassLoader() throws IOException { 
ClassLoader cl = this.getClass().getClassLoader(); 
Resource resource = new ClassPathResource("cn/javass/spring/chapter4/test1.properties" , cl); 
if(resource.exists()) { 
dumpStream(resource); 
} 
System.out.println("path:" + resource.getFile().getAbsolutePath()); 
Assert.assertEquals(false, resource.isOpen()); 
} 

3)使用指定的類進行加載資源,將嘗試加載相對於當前類的路徑的資源:

Java代碼

@Test 
public void testClasspathResourceByClass() throws IOException { 
Class clazz = this.getClass(); 
Resource resource1 = new ClassPathResource("cn/javass/spring/chapter4/test1.properties" , clazz); 
if(resource1.exists()) { 
dumpStream(resource1); 
} 
System.out.println("path:" + resource1.getFile().getAbsolutePath()); 
Assert.assertEquals(false, resource1.isOpen()); 

Resource resource2 = new ClassPathResource("test1.properties" , this.getClass()); 
if(resource2.exists()) { 
dumpStream(resource2); 
} 
System.out.println("path:" + resource2.getFile().getAbsolutePath()); 
Assert.assertEquals(false, resource2.isOpen()); 
} 

“resource1”將加載cn/javass/spring/chapter4/cn/javass/spring/chapter4/test1.properties資源;“resource2”將加載“cn/javass/spring/chapter4/test1.properties”;

4)加載jar包裏的資源,首先在當前類路徑下找不到,最後纔到Jar包裏找,而且在第一個Jar包裏找到的將被返回:

Java代碼

@Test 
public void classpathResourceTestFromJar() throws IOException { 
Resource resource = new ClassPathResource("overview.html"); 
if(resource.exists()) { 
dumpStream(resource); 
} 
System.out.println("path:" + resource.getURL().getPath()); 
Assert.assertEquals(false, resource.isOpen()); 
} 

如果當前類路徑包含“overview.html”,在項目的“resources”目錄下,將加載該資源,否則將加載Jar包裏的“overview.html”,而且不能使用“resource.getFile()”,應該使用“resource.getURL()”,因爲資源不存在於文件系統而是存在於jar包裏,URL類似於“file:/C:/…/*.jar!/overview.html”。
類路徑一般都是相對路徑,即相對於類路徑或相對於當前類的路徑,因此如果使用“/test1.properties”帶前綴“/”的路徑,將自動刪除“/”得到“test1.properties”。

4.2.5 UrlResource
UrlResource代表URL資源,用於簡化URL資源訪問。“isOpen”永遠返回false,表示可多次讀取資源。
UrlResource一般支持如下資源訪問:
http:通過標準的http協議訪問web資源,如new UrlResource(“http://地址”);
ftp:通過ftp協議訪問資源,如new UrlResource(“ftp://地址”);
file:通過file協議訪問本地文件系統資源,如new UrlResource(“file:d:/test.txt”);
具體使用方法在此就不演示了,可以參考cn.javass.spring.chapter4.ResourceTest中urlResourceTest測試方法。

4.2.6 ServletContextResource
ServletContextResource代表web應用資源,用於簡化servlet容器的ServletContext接口的getResource操作和getResourceAsStream操作;在此就不具體演示了。

4.2.7 VfsResource
VfsResource代表Jboss 虛擬文件系統資源。

Jboss VFS(Virtual File System)框架是一個文件系統資源訪問的抽象層,它能一致的訪問物理文件系統、jar資源、zip資源、war資源等,VFS能把這些資源一致的映射到一個目錄上,訪問它們就像訪問物理文件資源一樣,而其實這些資源不存在於物理文件系統。
在示例之前需要準備一些jar包,在此我們使用的是Jboss VFS3版本,可以下載最新的Jboss AS 6x,拷貝lib目錄下的“jboss-logging.jar”和“jboss-vfs.jar”兩個jar包拷貝到我們項目的lib目錄中並添加到“Java Build Path”中的“Libaries”中。
讓我們看下示例(cn.javass.spring.chapter4.ResourceTest):

Java代碼

@Test 
public void testVfsResourceForRealFileSystem() throws IOException { 
//1.創建一個虛擬的文件目錄 
VirtualFile home = VFS.getChild("/home"); 
//2.將虛擬目錄映射到物理的目錄 
VFS.mount(home, new RealFileSystem(new File("d:"))); 
//3.通過虛擬目錄獲取文件資源 
VirtualFile testFile = home.getChild("test.txt"); 
//4.通過一致的接口訪問 
Resource resource = new VfsResource(testFile); 
if(resource.exists()) { 
dumpStream(resource); 
} 
System.out.println("path:" + resource.getFile().getAbsolutePath()); 
Assert.assertEquals(false, resource.isOpen()); 
} 
@Test 
public void testVfsResourceForJar() throws IOException { 
//1.首先獲取jar包路徑 
File realFile = new File("lib/org.springframework.beans-3.0.5.RELEASE.jar"); 
//2.創建一個虛擬的文件目錄 
VirtualFile home = VFS.getChild("/home2"); 
//3.將虛擬目錄映射到物理的目錄 
VFS.mountZipExpanded(realFile, home, 
TempFileProvider.create("tmp", Executors.newScheduledThreadPool(1))); 
//4.通過虛擬目錄獲取文件資源 
VirtualFile testFile = home.getChild("META-INF/spring.handlers"); 
Resource resource = new VfsResource(testFile); 
if(resource.exists()) { 
dumpStream(resource); 
} 
System.out.println("path:" + resource.getFile().getAbsolutePath()); 
Assert.assertEquals(false, resource.isOpen()); 
} 

通過VFS,對於jar裏的資源和物理文件系統訪問都具有一致性,此處只是簡單示例,如果需要請到Jboss官網深入學習。
4.3.1 ResourceLoader接口
ResourceLoader接口用於返回Resource對象;其實現可以看作是一個生產Resource的工廠類。

Java代碼

public interface ResourceLoader { 
Resource getResource(String location); 
ClassLoader getClassLoader(); 
} 

getResource接口用於根據提供的location參數返回相應的Resource對象;而getClassLoader則返回加載這些Resource的ClassLoader。

Spring提供了一個適用於所有環境的DefaultResourceLoader實現,可以返回ClassPathResource、UrlResource;還提供一個用於web環境的ServletContextResourceLoader,它繼承了DefaultResourceLoader的所有功能,又額外提供了獲取ServletContextResource的支持。

ResourceLoader在進行加載資源時需要使用前綴來指定需要加載:“classpath:path”表示返回ClasspathResource,“http://path”和“file:path”表示返回UrlResource資源,如果不加前綴則需要根據當前上下文來決定,DefaultResourceLoader默認實現可以加載classpath資源,如代碼所示(cn.javass.spring.chapter4.ResourceLoaderTest):

Java代碼

@Test 
public void testResourceLoad() { 
ResourceLoader loader = new DefaultResourceLoader(); 
Resource resource = loader.getResource("classpath:cn/javass/spring/chapter4/test1.txt"); 
//驗證返回的是ClassPathResource 
Assert.assertEquals(ClassPathResource.class, resource.getClass()); 
Resource resource2 = loader.getResource("file:cn/javass/spring/chapter4/test1.txt"); 
//驗證返回的是ClassPathResource 
Assert.assertEquals(UrlResource.class, resource2.getClass()); 
Resource resource3 = loader.getResource("cn/javass/spring/chapter4/test1.txt"); 
//驗證返默認可以加載ClasspathResource 
Assert.assertTrue(resource3 instanceof ClassPathResource); 
} 

對於目前所有ApplicationContext都實現了ResourceLoader,因此可以使用其來加載資源。
ClassPathXmlApplicationContext:不指定前綴將返回默認的ClassPathResource資源,否則將根據前綴來加載資源;
FileSystemXmlApplicationContext:不指定前綴將返回FileSystemResource,否則將根據前綴來加載資源;
WebApplicationContext:不指定前綴將返回ServletContextResource,否則將根據前綴來加載資源;
其他:不指定前綴根據當前上下文返回Resource實現,否則將根據前綴來加載資源。

4.3.2 ResourceLoaderAware接口
ResourceLoaderAware是一個標記接口,用於通過ApplicationContext上下文注入ResourceLoader。

Java代碼

public interface ResourceLoaderAware { 
void setResourceLoader(ResourceLoader resourceLoader); 
} 

讓我們看下測試代碼吧:

1) 首先準備測試Bean,我們的測試Bean還簡單隻需實現ResourceLoaderAware接口,然後通過回調將ResourceLoader保存下來就可以了:

Java代碼

package cn.javass.spring.chapter4.bean; 
import org.springframework.context.ResourceLoaderAware; 
import org.springframework.core.io.ResourceLoader; 
public class ResourceBean implements ResourceLoaderAware { 
private ResourceLoader resourceLoader; 
@Override 
public void setResourceLoader(ResourceLoader resourceLoader) { 
this.resourceLoader = resourceLoader; 
} 
public ResourceLoader getResourceLoader() { 
return resourceLoader; 
} 
} 

2) 配置Bean定義(chapter4/resourceLoaderAware.xml):

Java代碼

<bean class="cn.javass.spring.chapter4.bean.ResourceBean"/> 

3)測試(cn.javass.spring.chapter4.ResoureLoaderAwareTest):

Java代碼

@Test 
public void test() { 
ApplicationContext ctx = new ClassPathXmlApplicationContext("chapter4/resourceLoaderAware.xml"); 
ResourceBean resourceBean = ctx.getBean(ResourceBean.class); 
ResourceLoader loader = resourceBean.getResourceLoader(); 
Assert.assertTrue(loader instanceof ApplicationContext); 
} 

注意此處“loader instanceof ApplicationContext”,說明了ApplicationContext就是個ResoureLoader。
由於上述實現回調接口注入ResourceLoader的方式屬於侵入式,所以不推薦上述方法,可以採用更好的自動注入方式,如“byType”和“constructor”,此處就不演示了。

4.3.3 注入Resource
通過回調或注入方式注入“ResourceLoader”,然後再通過“ResourceLoader”再來加載需要的資源對於只需要加載某個固定的資源是不是很麻煩,有沒有更好的方法類似於前邊實例中注入“java.io.File”類似方式呢?

Spring提供了一個PropertyEditor “ResourceEditor”用於在注入的字符串和Resource之間進行轉換。因此可以使用注入方式注入Resource。

ResourceEditor完全使用ApplicationContext根據注入的路徑字符串獲取相應的Resource,說白了還是自己做還是容器幫你做的問題。

接下讓我們看下示例:
1)準備Bean:

Java代碼

package cn.javass.spring.chapter4.bean; 
import org.springframework.core.io.Resource; 
public class ResourceBean3 { 
private Resource resource; 
public Resource getResource() { 
return resource; 
} 
public void setResource(Resource resource) { 
this.resource = resource; 
} 
} 

2)準備配置文件(chapter4/ resourceInject.xml):

Java代碼

<bean id="resourceBean1" class="cn.javass.spring.chapter4.bean.ResourceBean3"> 
<property name="resource" value="cn/javass/spring/chapter4/test1.properties"/> 
</bean> 
<bean id="resourceBean2" class="cn.javass.spring.chapter4.bean.ResourceBean3"> 
<property name="resource" 
value="classpath:cn/javass/spring/chapter4/test1.properties"/> 
</bean> 

注意此處“resourceBean1”注入的路徑沒有前綴表示根據使用的ApplicationContext實現進行選擇Resource實現。

3)讓我們來看下測試代碼(cn.javass.spring.chapter4.ResourceInjectTest)吧:

Java代碼

@Test 
public void test() { 
ApplicationContext ctx = new ClassPathXmlApplicationContext("chapter4/resourceInject.xml"); 
ResourceBean3 resourceBean1 = ctx.getBean("resourceBean1", ResourceBean3.class); 
ResourceBean3 resourceBean2 = ctx.getBean("resourceBean2", ResourceBean3.class); 
Assert.assertTrue(resourceBean1.getResource() instanceof ClassPathResource); 
Assert.assertTrue(resourceBean2.getResource() instanceof ClassPathResource); 
} 

接下來一節讓我們深入ApplicationContext對各種Resource的支持,及如何使用更便利的資源加載方式。
4.4.1 使用路徑通配符加載Resource
前面介紹的資源路徑都是非常簡單的一個路徑匹配一個資源,Spring還提供了一種更強大的Ant模式通配符匹配,從能一個路徑匹配一批資源。

Ant路徑通配符支持“?”、“”、“*”,注意通配符匹配不包括目錄分隔符“/”:

“?”:匹配一個字符,如“config?.xml”將匹配“config1.xml”;
”:匹配零個或多個字符串,如“cn//config.xml”將匹配“cn/javass/config.xml”,但不匹配匹配“cn/config.xml”;而“cn/config-*.xml”將匹配“cn/config-dao.xml”;
”:匹配路徑中的零個或多個目錄,如“cn//config.xml”將匹配“cn /config.xml”,也匹配“cn/javass/spring/config.xml”;而“cn/javass/config-.xml”將匹配“cn/javass/config-dao.xml”,即把“”當做兩個“*”處理。

Spring提供AntPathMatcher來進行Ant風格的路徑匹配。具體測試請參考cn.javass.spring.chapter4. AntPathMatcherTest。

Spring在加載類路徑資源時除了提供前綴“classpath:”的來支持加載一個Resource,還提供一個前綴“classpath*:”來支持加載所有匹配的類路徑Resource。

Spring提供ResourcePatternResolver接口來加載多個Resource,該接口繼承了ResourceLoader並添加了“Resource[] getResources(String locationPattern)”用來加載多個Resource:

public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = “classpath*:”;
Resource[] getResources(String locationPattern) throws IOException;
}

Spring提供了一個ResourcePatternResolver實現PathMatchingResourcePatternResolver,它是基於模式匹配的,默認使用AntPathMatcher進行路徑匹配,它除了支持ResourceLoader支持的前綴外,還額外支持“classpath*:”用於加載所有匹配的類路徑Resource,ResourceLoader不支持前綴“classpath*:”:

首先做下準備工作,在項目的“resources”創建“META-INF”目錄,然後在其下創建一個“INDEX.LIST”文件。同時在“org.springframework.beans-3.0.5.RELEASE.jar”和“org.springframework.context-3.0.5.RELEASE.jar”兩個jar包裏也存在相同目錄和文件。然後創建一個“LICENSE”文件,該文件存在於“com.springsource.cn.sf.cglib-2.2.0.jar”裏。

一、“classpath”: 用於加載類路徑(包括jar包)中的一個且僅一個資源;對於多個匹配的也只返回一個,所以如果需要多個匹配的請考慮“classpath*:”前綴;

@Test 
public void testClasspathPrefix() throws IOException { 
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); 
//只加載一個絕對匹配Resource,且通過ResourceLoader.getResource進行加載 
Resource[] resources=resolver.getResources("classpath:META-INF/INDEX.LIST"); 
Assert.assertEquals(1, resources.length); 
//只加載一個匹配的Resource,且通過ResourceLoader.getResource進行加載 
resources = resolver.getResources("classpath:META-INF/*.LIST"); 
Assert.assertTrue(resources.length == 1); 
} 

二、“classpath*”: 用於加載類路徑(包括jar包)中的所有匹配的資源。帶通配符的classpath使用“ClassLoader”的“Enumeration getResources(String name)”方法來查找通配符之前的資源,然後通過模式匹配來獲取匹配的資源。如“classpath:META-INF/*.LIST”將首先加載通配符之前的目錄“META-INF”,然後再遍歷路徑進行子路徑匹配從而獲取匹配的資源。

@Test 
public void testClasspathAsteriskPrefix () throws IOException { 
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); 
//將加載多個絕對匹配的所有Resource 
//將首先通過ClassLoader.getResources("META-INF")加載非模式路徑部分 
//然後進行遍歷模式匹配 
Resource[] resources=resolver.getResources("classpath*:META-INF/INDEX.LIST"); 
Assert.assertTrue(resources.length > 1); 
//將加載多個模式匹配的Resource 
resources = resolver.getResources("classpath*:META-INF/*.LIST"); 
Assert.assertTrue(resources.length > 1); 
} 

注意“resources.length >1”說明返回多個Resource。不管模式匹配還是非模式匹配只要匹配的都將返回。

在“com.springsource.cn.sf.cglib-2.2.0.jar”裏包含“asm-license.txt”文件,對於使用“classpath*: asm-*.txt”進行通配符方式加載資源將什麼也加載不了“asm-license.txt”文件,注意一定是模式路徑匹配纔會遇到這種問題。這是由於“ClassLoader”的“getResources(String name)”方法的限制,對於name爲“”的情況將只返回文件系統的類路徑,不會包換jar包根路徑。

@Test 
public void testClasspathAsteriskPrefixLimit() throws IOException { 
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); //將首先通過ClassLoader.getResources("")加載目錄, 
//將只返回文件系統的類路徑不返回jar的跟路徑 
//然後進行遍歷模式匹配 
Resource[] resources = resolver.getResources("classpath*:asm-*.txt"); 
Assert.assertTrue(resources.length == 0); 
//將通過ClassLoader.getResources("asm-license.txt")加載 
//asm-license.txt存在於com.springsource.net.sf.cglib-2.2.0.jar 
resources = resolver.getResources("classpath*:asm-license.txt"); 
Assert.assertTrue(resources.length > 0); 
//將只加載文件系統類路徑匹配的Resource 
resources = resolver.getResources("classpath*:LICENS*"); 
Assert.assertTrue(resources.length == 1); 
}

對於“resolver.getResources(“classpath*:asm-.txt”);”,由於在項目“resources”目錄下沒有所以應該返回0個資源;“resolver.getResources(“classpath:asm-license.txt”);”將返回jar包裏的Resource;“resolver.getResources(“classpath*:LICENS*”);”,因爲將只返回文件系統類路徑資源,所以返回1個資源。

因此加載通配符路徑時(即路徑中包含通配符),必須包含一個根目錄才能保證加載的資源是所有的,而不是部分。

三、“file”:加載一個或多個文件系統中的Resource。如“file:D:/*.txt”將返回D盤下的所有txt文件;

四、無前綴:通過ResourceLoader實現加載一個資源。

AppliacationContext提供的getResources方法將獲取資源委託給ResourcePatternResolver實現,默認使用PathMatchingResourcePatternResolver。所有在此就無需介紹其使用方法了。

4.4.2 注入Resource數組
Spring還支持注入Resource數組,直接看配置如下:

<bean id="resourceBean1" class="cn.javass.spring.chapter4.bean.ResourceBean4"> 
<property name="resources"> 
<array> 
<value>cn/javass/spring/chapter4/test1.properties</value> 
<value>log4j.xml</value> 
</array> 
</property> 
</bean> 
<bean id="resourceBean2" class="cn.javass.spring.chapter4.bean.ResourceBean4"> 
<property name="resources" value="classpath*:META-INF/INDEX.LIST"/> 
</bean> 
<bean id="resourceBean3" class="cn.javass.spring.chapter4.bean.ResourceBean4"> 
<property name="resources"> 
<array> 
<value>cn/javass/spring/chapter4/test1.properties</value> 
<value>classpath*:META-INF/INDEX.LIST</value> 
</array> 
</property> 
</bean> 

“resourceBean1”就不用多介紹了,傳統實現方式;對於“resourceBean2”則使用前綴“classpath*”,看到這大家應該懂的,加載匹配多個資源;“resourceBean3”是混合使用的;測試代碼在“cn.javass.spring.chapter4.ResourceInjectTest.testResourceArrayInject”。
Spring通過ResourceArrayPropertyEditor來進行類型轉換的,而它又默認使用“PathMatchingResourcePatternResolver”來進行把路徑解析爲Resource對象。所有大家只要會使用“PathMatchingResourcePatternResolver”,其它一些實現都是委託給它的,比如AppliacationContext的“getResources”方法等。

4.4.3 AppliacationContext實現對各種Resource的支持
一、ClassPathXmlApplicationContext:默認將通過classpath進行加載返回ClassPathResource,提供兩類構造器方法:

public class ClassPathXmlApplicationContext { 
//1)通過ResourcePatternResolver實現根據configLocation獲取資源 
public ClassPathXmlApplicationContext(String configLocation); 
public ClassPathXmlApplicationContext(String... configLocations); 
public ClassPathXmlApplicationContext(String[] configLocations, ……); 

//2)通過直接根據path直接返回ClasspathResource 
public ClassPathXmlApplicationContext(String path, Class clazz); 
public ClassPathXmlApplicationContext(String[] paths, Class clazz); 
public ClassPathXmlApplicationContext(String[] paths, Class clazz, ……); 
} 

第一類構造器是根據提供的配置文件路徑使用“ResourcePatternResolver ”的“getResources()”接口通過匹配獲取資源;即如“classpath:config.xml”
第二類構造器則是根據提供的路徑和clazz來構造ClassResource資源。即採用“public ClassPathResource(String path, Class

**public class FileSystemXmlApplicationContext{ 
public FileSystemXmlApplicationContext(String configLocation); 
public FileSystemXmlApplicationContext(String... configLocations,……); 
} 

**

//linux系統,以下全是相對於當前vm路徑進行加載
new FileSystemXmlApplicationContext(“chapter4/config.xml”);
new FileSystemXmlApplicationContext(“/chapter4/confg.xml”);

//windows系統,第一個將相對於當前vm路徑進行加載;
//第二個則是絕對路徑方式加載
new FileSystemXmlApplicationContext(“chapter4/config.xml”);
new FileSystemXmlApplicationContext(“d:/chapter4/confg.xml”);

此處還需要注意:在linux系統上,構造器使用的是相對路徑,而ctx.getResource()方法如果以“/”開頭則表示獲取絕對路徑資源,而不帶前導“/”將返回相對路徑資源。如下:

//linux系統,第一個將相對於當前vm路徑進行加載;
//第二個則是絕對路徑方式加載
ctx.getResource (“chapter4/config.xml”);
ctx.getResource (“/root/confg.xml”);
//windows系統,第一個將相對於當前vm路徑進行加載;
//第二個則是絕對路徑方式加載
ctx.getResource (“chapter4/config.xml”);
ctx.getResource (“d:/chapter4/confg.xml”);

因此如果需要加載絕對路徑資源最好選擇前綴“file”方式,將全部根據絕對路徑加載。如在linux系統“ctx.getResource (“file:/root/confg.xml”);”

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章