uml
先来张图看看Resouce接口的继承体系结构。类都来自spirng core包
Resource作为spirng的统一资源抽象接口,在AbstractResource类中提供了默认实现。类结构图中实现不少,都介绍下
-
FileSystemResource
用来处理java.io.File,支持解析File和URL,并且从5.0版本开始使用NIO2进行读写交互
-
PathResource 用来处理java.nio.file.Path,支持解析File也可以解析URL
-
UrlResource
作为java.net.URL的定位器,支持解析java.io.File也支持解析URL
-
ClassPathResource 用来处理classpath的资源,用ClassLoader或一个Class来加载资源,支持解析classpath下的java.io.File,也支持URL,但是不支持解析jar中的资源文件。
-
ByteArrayResource
用来从给定的二进制数组加载内容
-
InputStreamResource
用来处理给定的InputStream,通常用来处理一些特别的资源,不然最好使用一些专门的Resource,如ClassPathResource
-
VfsResource
用来处理jboss vfs的资源
Resource接口
下面来看看Resource接口提供的能力,截图有点长,直接复制代码
public interface Resource extends InputStreamSource {
/**
* Determine 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.
*
* 检查resource是否真实存在
*/
boolean exists();
/**
* Indicate whether the contents of this resource can be read via
* {@link #getInputStream()}.
* <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()
*
* resource是否可读取
*/
default boolean isReadable() {
return true;
}
/**
* Indicate whether this resource represents a handle with an open stream.
* If {@code 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.
* 资源代表的句柄是否被一个stream打开了
*/
default boolean isOpen() {
return false;
}
/**
* Determine whether this resource represents a file in a file system.
* A value of {@code true} strongly suggests (but does not guarantee)
* that a {@link #getFile()} call will succeed.
* <p>This is conservatively {@code false} by default.
* @since 5.0
* @see #getFile()
*
* resource是否是一个File
*/
default boolean isFile() {
return false;
}
/**
* 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
* 返回resource的url的句柄
*/
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
* @since 2.5
*
* 返回resource的URL的句柄
*/
URI getURI() throws IOException;
/**
* Return a File handle for this resource.
* @throws java.io.FileNotFoundException if the resource cannot be resolved as
* absolute file path, i.e. if the resource is not available in a file system
* @throws IOException in case of general resolution/reading failures
* @see #getInputStream()
*
* 返回resource的File的句柄
*/
File getFile() throws IOException;
/**
* Return a {@link ReadableByteChannel}.
* <p>It is expected that each call creates a <i>fresh</i> channel.
* <p>The default implementation returns {@link Channels#newChannel(InputStream)}
* with the result of {@link #getInputStream()}.
* @return the byte channel for the underlying resource (must not be {@code null})
* @throws java.io.FileNotFoundException if the underlying resource doesn't exist
* @throws IOException if the content channel could not be opened
* @since 5.0
* @see #getInputStream()
*
* 返回ReadableByteChannel
*/
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
/**
* 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)
*
* 返回resource内容的长度
*/
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)
*
* 返回resource最后修改的时间戳
*/
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的相对路径创建新的resource
*/
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.
*
* 返回resource的文件名
*/
@Nullable
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()
*
* 返回resource的描述
*/
String getDescription();
}
默认实现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.
*
* 判断文件是否存在,若产生异常就关闭流
*/
@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 {
getInputStream().close();
return true;
}
catch (Throwable isEx) {
return false;
}
}
}
/**
* This implementation always returns {@code true}.
*
* 默认返回true,表示可读
*/
@Override
public boolean isReadable() {
return true;
}
/**
* This implementation always returns {@code false}.
*
* 默认返回false,表示未被打开
*/
@Override
public boolean isOpen() {
return false;
}
/**
* This implementation always returns {@code false}.
*
* 默认返回false,表示不是 File
*/
@Override
public boolean isFile() {
return false;
}
/**
* This implementation throws a FileNotFoundException, assuming
* that the resource cannot be resolved to a URL.
*
* 抛出 FileNotFoundException 异常,交给子类实现
*/
@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()}.
*
* 基于 getURL() 返回的url构造URI
*/
@Override
public URI getURI() throws IOException {
// getURL()默认实现会直接抛出 FileNotFoundException 异常
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.
*
* 抛出 FileNotFoundException 异常,交给子类实现
*/
@Override
public File getFile() throws IOException {
throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
}
/**
* This implementation returns {@link Channels#newChannel(InputStream)}
* with the result of {@link #getInputStream()}.
* <p>This is the same as in {@link Resource}'s corresponding default method
* but mirrored here for efficient JVM-level dispatching in a class hierarchy.
*
* 通过 getInputStream() 返回的 InputStream 构造 ReadableByteChannel
*/
@Override
public ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(getInputStream());
}
/**
* 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()
*
* 获取资源正文长度
*
* 实际就是资源正文的字节长度,通过循环读取一遍来计算
*/
@Override
public long contentLength() throws IOException {
InputStream is = getInputStream();
try {
long size = 0;
// 构造一个256长度的字节数字
byte[] buf = new byte[256];
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 {
// 拿到要获取最后修改时间的资源File
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;
}
/**
* 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 FileNotFoundException if the resource cannot be resolved as
* an absolute file path, i.e. is not available in a file system
* @throws IOException in case of general resolution/reading failures
*
* 获取资源File(拿到要获取最后修改时间的资源File)
*/
protected File getFileForLastModifiedCheck() throws IOException {
return getFile();
}
/**
* This implementation throws a FileNotFoundException, assuming
* that relative resources cannot be created for this resource.
*
* 根据相对路径构造Resource,默认实现抛出 FileNotFoundException 异常,交个子类实现
*/
@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.
*
* 获取资源名称,默认返回null,交给子类实现
*/
@Override
@Nullable
public String getFilename() {
return null;
}
/**
* This implementation compares description strings.
* @see #getDescription()
*/
@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();
}
/**
* This implementation returns the description of this resource.
* @see #getDescription()
*
* 返回资源描述
*/
@Override
public String toString() {
return getDescription();
}
}
有没有注意到spring的Resource接口使用了策略模式呢!哈哈,Resource接口的实现类就是具体的策略类,而ApplicationContext就是策略的context。 注意:如果要实现自定义的资源策略,最好是直接继承AbstractResource而不是实现Resource接口(这句话是看别人写的,目前笔者不是很清楚原因-_-!)