对于很多程序框架来讲,资源访问都是比较基础的。JDK提供了资源访问类(如java.net.URL、File类等)。但是它并不能很好的满足各种底层资源的访问需求,比如缺少从类路径或容器上下文中获取资源的操作类。Spring设计了一个Resource接口,类结构如下:
其实结构Resource 继承了InputStreamSource。所以,我们先看看InputStreamSource,代码如下
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
很简单,就一个方法,意思就是规定必须能够读取资源。
下面看看Resource结构。
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();
}
其实也很简单,就是规定了一下资源访问的常用方法。从名字也就知道了它们大概能干嘛。
isReadable()是看看这个资源是否可读。
isOpen()看看这个资源是否已经打开了,如果已经打开了。那么就不能再打开了。
createTelative(String relativePath) Create a resource relative to this resource.
抽象类AbstractResource非常关键,如果不是这个类的话,很多相似的实现,就需要各个类独自实现了,造成了大量的代码重复。
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.
*/
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</code>.
*/
public boolean isReadable() {
return true;
}
/**
* This implementation always returns <code>false</code>.
*/
public boolean isOpen() {
return false;
}
/**
* This implementation throws a FileNotFoundException, assuming
* that the resource cannot be resolved to a URL.
*/
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()}.
*/
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.
*/
public File getFile() throws IOException {
throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
}
/**
* This implementation checks the timestamp of the underlying File,
* if available.
* @see #getFile()
*/
public long contentLength() throws IOException {
return getFile().length();
}
/**
* This implementation checks the timestamp of the underlying File,
* if available.
* @see #getFileForLastModifiedCheck()
*/
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</code>)
* @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.
*/
public Resource createRelative(String relativePath) throws IOException {
throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());
}
/**
* This implementation always throws IllegalStateException,
* assuming that the resource does not have a filename.
*/
public String getFilename() throws IllegalStateException {
throw new IllegalStateException(getDescription() + " does not have a filename");
}
/**
* 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();
}
}
这样,各个方法就都有了一个简单的实现了。
看一个具体实现FileSystemResource
public class FileSystemResource extends AbstractResource {
private final File file;
private final String path;
public FileSystemResource(File file) {
Assert.notNull(file, "File must not be null");
this.file = file;
this.path = StringUtils.cleanPath(file.getPath());
}
public FileSystemResource(String path) {
Assert.notNull(path, "Path must not be null");
this.file = new File(path);
this.path = StringUtils.cleanPath(path);
}
public final String getPath() {
return this.path;
}
@Override
public boolean exists() {
return this.file.exists();
}
@Override
public boolean isReadable() {
return (this.file.canRead() && !this.file.isDirectory());
}
public InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}
@Override
public URL getURL() throws IOException {
return this.file.toURI().toURL();
}
@Override
public URI getURI() throws IOException {
return this.file.toURI();
}
@Override
public File getFile() {
return this.file;
}
/**
* This implementation creates a FileSystemResource, applying the given path
* relative to the path of the underlying file of this resource descriptor.
* @see org.springframework.util.StringUtils#applyRelativePath(String, String)
*/
@Override
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
return new FileSystemResource(pathToUse);
}
@Override
public String getFilename() {
return this.file.getName();
}
/**
* This implementation returns a description that includes the absolute
* path of the file.
* @see java.io.File#getAbsolutePath()
*/
public String getDescription() {
return "file [" + this.file.getAbsolutePath() + "]";
}
/**
* This implementation compares the underlying File references.
*/
@Override
public boolean equals(Object obj) {
return (obj == this ||
(obj instanceof FileSystemResource && this.path.equals(((FileSystemResource) obj).path)));
}
/**
* This implementation returns the hash code of the underlying File reference.
*/
@Override
public int hashCode() {
return this.path.hashCode();
}
}
方法都还是比较简单的。