Resource
接口定義如下
package org.springframework.core.io;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import org.springframework.lang.Nullable;
public interface Resource extends InputStreamSource {
boolean exists();
default boolean isReadable() {
return this.exists();
}
default boolean isOpen() {
return false;
}
default boolean isFile() {
return false;
}
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(this.getInputStream());
}
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String var1) throws IOException;
@Nullable
String getFilename();
String getDescription();
}
如果你讀過Spring源碼,那麼對Resource接口你就不會陌生,特别在資源加載環節,到處是Resource的身影。
常見Resource
- UrlResource:包裝了java.net.URL,可以通路通過URL通路的任何對象。比如:檔案、Http連結位址、FTP等等。
- ClassPathResource:從類路徑中擷取的資源。它使用線程上下文類加載器、給定的類加載器或給定的類來加載資源。
- FileSystemResource:支援file:///開頭的讀取系統檔案(如:file:///c:/work/aaa.txt)
直接使用Resource對象
@Test
public void testUrlResource(){
try {
// 讀取CSDN部落格文章
UrlResource urlResource = new UrlResource("https://blog.csdn.net/forlinkext/article/details/118727625");
print(urlResource);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
@Test
public void testClassPathResource() {
// 讀取resource目錄下(classPath根路徑)的test.properties檔案
ClassPathResource classPathResource = new ClassPathResource("test.properties");
print(classPathResource);
}
@Test
public void testFileSystemResource() {
// 讀取系統檔案c:/work/aaa.txt
FileSystemResource classPathResource = new FileSystemResource("c:/work/aaa.txt");
print(classPathResource);
}
public void print(Resource resource) {
try (BufferedReader r = new BufferedReader(new InputStreamReader(resource.getInputStream()))){
String str;
while ((str = r.readLine()) != null) {
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
如果aaa.txt檔案中包含中文,我們的代碼列印可能會出現亂碼,請使用InputStreamReader的帶編碼參數的構造函數來指定編碼格式。
注:列印代碼中我們使用了try-with-resouce機制(jdk1.7版本及以上才支援)
ResourceLoader
接口定義如下
package org.springframework.core.io;
import org.springframework.lang.Nullable;
public interface ResourceLoader {
String CLASSPATH_URL_PREFIX = "classpath:";
Resource getResource(String var1);
@Nullable
ClassLoader getClassLoader();
}
ResourceLoader定義了如何擷取Resource,它支援一些特定的字首,如"classpath:"等。
我們前面是有的ApplicationContext都實作了該接口。意味着我們可以通過context.getResource方法來擷取我們想要的Resource對象。
@Test
public void ctxTest1() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
Resource resource = context.getResource("https://blog.csdn.net/forlinkext/article/details/118727625");
System.out.println(resource.getClass());// UrlResource
print(resource);
}
@Test
public void ctxTest2() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
Resource resource = context.getResource("file:///c:/work/aaa.txt");
System.out.println(resource.getClass()); // FileUrlResource
print(resource);
}
@Test
public void ctxTest3() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
Resource resource = context.getResource("test.properties");
System.out.println(resource.getClass()); // DefaultResourceLoader$ClassPathContextResource
print(resource);
}
@Test
public void ctxTest4() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
Resource resource = context.getResource("classpath:test.properties");
System.out.println(resource.getClass()); // ClassPathResource
print(resource);
}
可以看到我們使用了同樣的方法,但是卻得到了不同的Resource實作,根據我們的String參數的不同得到的Resource實作也不同。我們示例中有https開頭、file開頭、classpath:開頭,對應得到的Resource實作也不一樣。這是由PropertyEditor最終決定的。PropertyEditor我們将在後續講解。
ResourcePatternResolver接口
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException;
}
上面執行個體中我們使用了classpath:,ResourcePatternResolver接口中定義了calasspath*:,它們有什麼差別嗎。我們先來看看如下的demo
@Test
public void ctxTest5() throws IOException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
Resource[] resource = context.getResources("classpath*:test.properties");
System.out.println(resource[0].getClass()); // UrlResource
print(resource[0]);
}
@Test
public void ctxTest6() throws IOException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
Resource[] resource = context.getResources("classpath*:*.properties");
if(resource != null) {
System.out.println(resource.length);
for(int i = 0 ; i < resource.length;i++) {
System.out.println("========================");
System.out.println(resource[i].getFilename());
System.out.println(resource[i].getClass());
print(resource[i]);
}
}
}
注意:
- classpath*:,需要使用getResources方法
- getResource方法使用classpath*:将會報錯
- classpath*:可以擷取在依賴的jar包中的資源
- classpath*:可以與通配符共用來實作模糊比對,如classpath:spring-*.properties
- classpath:不能與*通配符共用。
使用注解來擷取Resource對象
package com.yyoo.boot.annotation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
@Component
public class MyResourceBean {
@Value("${my.resource.path}")
public Resource[] resources;
public Resource[] getResources(){
return this.resources;
}
}
注意:我們的示例配置my.resource.path = classpath*:test.properties,如果路徑是classpath*:,則需要使用Resource[] 數組接收,否則會報錯。
注意:将Resource這裡的資源擷取跟@PropertySource注解導入配置區分開來。Resource可以不止是Properties配置檔案,還可以是其他任何的https、ftp或本地、classpath路徑下的任意檔案。
總結
我們在使用中盡量不要自己new Resource,通過ApplicationContext的getResource或getResources方法擷取,或者直接使用@Value注解配合配置檔案來實作。
classpath*:支援通配符比對,而且可以掃描不同位置的多個同名資源。
上一篇:009-Spring IoC 國際化(i18n)
下一篇:待續