1.ResourceLoader 资源加载器接口
public interface ResourceLoader {
//从类路径加载资源前缀
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
//返回一个要处理的资源对象
Resource getResource(String location);
//暴露出一个类加载器
ClassLoader getClassLoader();
}
2.ProtocolResolver 协议解析接口
public interface ProtocolResolver {
//根据指定位置解析和resouceLoader返回一个Resouce对象
Resource resolve(String location, ResourceLoader resourceLoader);
}
3.InputStreamSource 输入源
public interface InputStreamSource {
//Return An InputStream
InputStream getInputStream() throws IOException;
}
4.Resource 资源
public interface Resource extends InputStreamSource {
/**
* Return whether this resource actually exists in physical form.
* <p>This method performs a definitive existence check, whereas the
* existence of a {@code Resource} handle only guarantees a
* valid descriptor handle.
*/
boolean exists();
/**
* Return whether the contents of this resource can be read,
* e.g. via {@link #getInputStream()} or {@link #getFile()}.
* <p>Will be {@code true} for typical resource descriptors;
* note that actual content reading may still fail when attempted.
* However, a value of {@code false} is a definitive indication
* that the resource content cannot be read.
* @see #getInputStream()
*/
boolean isReadable();
/**
* Return whether this resource represents a handle with an open
* stream. If true, the InputStream cannot be read multiple times,
* and must be read and closed to avoid resource leaks.
* <p>Will be {@code false} for typical resource descriptors.
*/
boolean isOpen();
/**
* Return a URL handle for this resource.
* @throws IOException if the resource cannot be resolved as URL,
* i.e. if the resource is not available as descriptor
*/
URL getURL() throws IOException;
/**
* Return a URI handle for this resource.
* @throws IOException if the resource cannot be resolved as URI,
* i.e. if the resource is not available as descriptor
*/
URI getURI() throws IOException;
/**
* Return a File handle for this resource.
* @throws IOException if the resource cannot be resolved as absolute
* file path, i.e. if the resource is not available in a file system
*/
File getFile() throws IOException;
/**
* Determine the content length for this resource.
* @throws IOException if the resource cannot be resolved
* (in the file system or as some other known physical resource type)
*/
long contentLength() throws IOException;
/**
* Determine the last-modified timestamp for this resource.
* @throws IOException if the resource cannot be resolved
* (in the file system or as some other known physical resource type)
*/
long lastModified() throws IOException;
/**
* Create a resource relative to this resource.
* @param relativePath the relative path (relative to this resource)
* @return the resource handle for the relative resource
* @throws IOException if the relative resource cannot be determined
*/
Resource createRelative(String relativePath) throws IOException;
/**
* Determine a filename for this resource, i.e. typically the last
* part of the path: for example, "myfile.txt".
* <p>Returns {@code null} if this type of resource does not
* have a filename.
*/
String getFilename();
/**
* Return a description for this resource,
* to be used for error output when working with the resource.
* <p>Implementations are also encouraged to return this value
* from their {@code toString} method.
* @see Object#toString()
*/
String getDescription();
5.AbstractResource 抽象类 对Resource的实现
public abstract class AbstractResource implements Resource {
/**
* This implementation checks whether a File can be opened,
* falling back to whether an InputStream can be opened.
* This will cover both directories and content resources.
*/
@Override
public boolean exists() {
// Try file existence: can we find the file in the file system?
try {
return getFile().exists();
}
catch (IOException ex) {
// Fall back to stream existence: can we open the stream?
try {
InputStream is = getInputStream();
is.close();
return true;
}
catch (Throwable isEx) {
return false;
}
}
}
/**
* This implementation always returns {@code true}.
*/
@Override
public boolean isReadable() {
return true;
}
/**
* This implementation always returns {@code false}.
*/
@Override
public boolean isOpen() {
return false;
}
/**
* This implementation throws a FileNotFoundException, assuming
* that the resource cannot be resolved to a URL.
*/
@Override
public URL getURL() throws IOException {
throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");
}
/**
* This implementation builds a URI based on the URL returned
* by {@link #getURL()}.
*/
@Override
public URI getURI() throws IOException {
URL url = getURL();
try {
return ResourceUtils.toURI(url);
}
catch (URISyntaxException ex) {
throw new NestedIOException("Invalid URI [" + url + "]", ex);
}
}
/**
* This implementation throws a FileNotFoundException, assuming
* that the resource cannot be resolved to an absolute file path.
*/
@Override
public File getFile() throws IOException {
throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
}
/**
* This implementation reads the entire InputStream to calculate the
* content length. Subclasses will almost always be able to provide
* a more optimal version of this, e.g. checking a File length.
* @see #getInputStream()
* @throws IllegalStateException if {@link #getInputStream()} returns null.
*/
@Override
public long contentLength() throws IOException {
InputStream is = getInputStream();
Assert.state(is != null, "Resource InputStream must not be null");
try {
long size = 0;
byte[] buf = new byte[255];
int read;
while ((read = is.read(buf)) != -1) {
size += read;
}
return size;
}
finally {
try {
is.close();
}
catch (IOException ex) {
}
}
}
/**
* This implementation checks the timestamp of the underlying File,
* if available.
* @see #getFileForLastModifiedCheck()
*/
@Override
public long lastModified() throws IOException {
long lastModified = getFileForLastModifiedCheck().lastModified();
if (lastModified == 0L) {
throw new FileNotFoundException(getDescription() +
" cannot be resolved in the file system for resolving its last-modified timestamp");
}
return lastModified;
}
/**
* Determine the File to use for timestamp checking.
* <p>The default implementation delegates to {@link #getFile()}.
* @return the File to use for timestamp checking (never {@code null})
* @throws IOException if the resource cannot be resolved as absolute
* file path, i.e. if the resource is not available in a file system
*/
protected File getFileForLastModifiedCheck() throws IOException {
return getFile();
}
/**
* This implementation throws a FileNotFoundException, assuming
* that relative resources cannot be created for this resource.
*/
@Override
public Resource createRelative(String relativePath) throws IOException {
throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());
}
/**
* This implementation always returns {@code null},
* assuming that this resource type does not have a filename.
*/
@Override
public String getFilename() {
return null;
}
/**
* This implementation returns the description of this resource.
* @see #getDescription()
*/
@Override
public String toString() {
return getDescription();
}
/**
* This implementation compares description strings.
* @see #getDescription()
*/
@Override
public boolean equals(Object obj) {
return (obj == this ||
(obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));
}
/**
* This implementation returns the description's hash code.
* @see #getDescription()
*/
@Override
public int hashCode() {
return getDescription().hashCode();
}
}