接口定义以下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
复制代码