接口定義以下java
/*
* @see #getInputStream()
* @see #getURL()
* @see #getURI()
* @see #getFile()
* @see WritableResource
* @see ContextResource
* @see UrlResource
* @see FileUrlResource
* @see FileSystemResource
* @see ClassPathResource
* @see ByteArrayResource
* @see InputStreamResource
*/
public interface Resource extends InputStreamSource {
/**
* 是否存在
*/
boolean exists();
/**
* 是否可讀
*/
default boolean isReadable() {
return exists();
}
/**
* 資源所表明的句柄是否被一個 stream 打開了
*/
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(getInputStream());
}
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String relativePath) throws IOException;
@Nullable
String getFilename();
String getDescription();
複製代碼
類圖以下 spring
其中express
public abstract class AbstractResource implements Resource {
/**
* 判斷文件是否存在,若判斷過程產生異常,就關閉對應的流
*/
@Override
public boolean exists() {
//判斷文件是否存在
try {
return getFile().exists();
}
catch (IOException ex) {
// 基於 InputStream 進行判斷
try {
getInputStream().close();
return true;
}
catch (Throwable isEx) {
return false;
}
}
}
/**
* 默承認讀
*/
@Override
public boolean isReadable() {
return exists();
}
/**
* 直接返回 false,表示未被打開
*/
@Override
public boolean isOpen() {
return false;
}
/**
* 直接返回False 表示不是一個文件
*/
@Override
public boolean isFile() {
return false;
}
/**
* 直接拋出異常,交給子類去實現
*/
@Override
public URL getURL() throws IOException {
throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");
}
/**
* 基於 getURL() 返回的 URL 構建 URI
*/
@Override
public URI getURI() throws IOException {
URL url = getURL();
try {
return ResourceUtils.toURI(url);
}
catch (URISyntaxException ex) {
throw new NestedIOException("Invalid URI [" + url + "]", ex);
}
}
/**
* 拋出異常 交於子類實現
*/
@Override
public File getFile() throws IOException {
throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
}
/**
* 根據 getInputStream() 的返回結果構建 ReadableByteChannel
*/
@Override
public ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
/**
* 資源的字節長度,經過所有讀取一遍來判斷
*/
@Override
public long contentLength() throws IOException {
InputStream is = getInputStream();
try {
long size = 0;
byte[] buf = new byte[256];
int read;
while ((read = is.read(buf)) != -1) {
size += read;
}
return size;
}
finally {
try {
is.close();
}
catch (IOException ex) {
}
}
}
/**
* 上次修改時間
*/
@Override
public long lastModified() throws IOException {
File fileToCheck = getFileForLastModifiedCheck();
long lastModified = fileToCheck.lastModified();
if (lastModified == 0L && !fileToCheck.exists()) {
throw new FileNotFoundException(getDescription() +
" cannot be resolved in the file system for checking its last-modified timestamp");
}
return lastModified;
}
/**
*
*/
protected File getFileForLastModifiedCheck() throws IOException {
return getFile();
}
/**
*
*/
@Override
public Resource createRelative(String relativePath) throws IOException {
throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());
}
/**
*
*/
@Override
@Nullable
public String getFilename() {
return null;
}
/**
*
*/
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof Resource &&
((Resource) other).getDescription().equals(getDescription())));
}
/**
* This implementation returns the description's hash code. * @see #getDescription() */ @Override public int hashCode() { return getDescription().hashCode(); } /** * */ @Override public String toString() { return getDescription(); } 複製代碼
Spring將資源的定位和加載進行了隔離數組
ResourceLoader源碼bash
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX; //默認 classpath:
Resource getResource(String location);
@Nullable
ClassLoader getClassLoader();
複製代碼
類圖以下ide
DefaultResourceLoader 是 ResourceLoader 的默認實現函數
public class DefaultResourceLoader implements ResourceLoader {
@Nullable
private ClassLoader classLoader;
private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);
private final Map<Class<?>, Map<Resource, ?>> resourceCaches = new ConcurrentHashMap<>(4);
@Nullable
private ClassLoader classLoader;
public DefaultResourceLoader() { // 無參構造函數
this.classLoader = ClassUtils.getDefaultClassLoader();
}
public DefaultResourceLoader(@Nullable ClassLoader classLoader) { // 帶 ClassLoader 參數的構造函數
this.classLoader = classLoader;
}
public void setClassLoader(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
}
public void addProtocolResolver(ProtocolResolver resolver) {
Assert.notNull(resolver, "ProtocolResolver must not be null");
this.protocolResolvers.add(resolver);
}
public Collection<ProtocolResolver> getProtocolResolvers() {
return this.protocolResolvers;
}
@Override
@Nullable
public ClassLoader getClassLoader() {
return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());
}
}
複製代碼
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
// 首先,經過 ProtocolResolver 來加載資源
for (ProtocolResolver protocolResolver : this.protocolResolvers) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
// 其次,以 / 開頭,返回 ClassPathContextResource 類型的資源
if (location.startsWith("/")) {
return getResourceByPath(location);
// 再次,以 classpath: 開頭,返回 ClassPathResource 類型的資源
} else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
// 而後,根據是否爲文件 URL ,是則返回 FileUrlResource 類型的資源,不然返回 UrlResource 類型的資源
} else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
} catch (MalformedURLException ex) {
// 最後,返回 ClassPathContextResource 類型的資源
return getResourceByPath(location);
}
}
}
複製代碼
protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
}
複製代碼
代碼路徑 org.springframework.core.io.ProtocolResolver
,做爲 DefaultResourceLoader, 實現自定義 Resource,無需集成AbstractResource,只需實現ProtocolResolver接口便可gradle
@FunctionalInterface
public interface ProtocolResolver {
/**
* 使用指定的 ResourceLoader ,解析指定的 location 若成功,則返回對應的 Resource
*/
@Nullable
Resource resolve(String location, ResourceLoader resourceLoader);
}
複製代碼
須要用戶自定義它的實現類,而後調用DefaultResourceLoader.addProtocolResolver(ProtocolResolver resolver)方法便可ui
#3.2 FileSystemResourceLoader FileSystemResourceLoader 繼承自DefaultResourceLoader 並覆寫了 getResourceByPath(String locaition)
方法this
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemContextResource(path);
}
/**
* FileSystemResource that explicitly expresses a context-relative path
* through implementing the ContextResource interface.
*/
private static class FileSystemContextResource extends FileSystemResource implements ContextResource {
public FileSystemContextResource(String path) {
super(path);
}
@Override
public String getPathWithinContext() {
return getPath();
}
}
複製代碼
##3.2.1 FileSystemContextResource 繼承自FileSystemResource 實現 ContextResource
##3.2.2 示例
public static void main(String[] args) {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource fileResource1 = resourceLoader.getResource("D:/Users/cindy/code/demo/build.gradle");
System.out.println("fileResource1 is FileSystemResource:" + (fileResource1 instanceof FileSystemResource));
Resource fileResource2 = resourceLoader.getResource("/Users/cindy/code/demo/build.gradle");
System.out.println("fileResource2 is ClassPathResource:" + (fileResource2 instanceof ClassPathResource));
Resource urlResource1 = resourceLoader.getResource("file:/Users/cindy/code/demo/build.gradle");
System.out.println("urlResource1 is UrlResource:" + (urlResource1 instanceof UrlResource));
Resource urlResource2 = resourceLoader.getResource("http://www.baidu.com");
System.out.println("urlResource1 is urlResource:" + (urlResource2 instanceof UrlResource));
}
複製代碼
fileResource1 is FileSystemResource:false
fileResource2 is ClassPathResource:true
urlResource1 is UrlResource:true
urlResource1 is urlResource:true
複製代碼
fileResource1 is FileSystemResource:true
fileResource2 is ClassPathResource:false
urlResource1 is UrlResource:true
urlResource1 is urlResource:true
複製代碼