概述
在日常程序开发中,处理外部资源是很繁琐的事情,我们可能需要处理URL资源、File资源资源、ClassPath相关资源、服务器相关资源(JBoss AS 5.x上的VFS资源)等等很多资源。因此处理这些资源需要使用不同的接口,这就增加了我们系统的复杂性;而且处理这些资源步骤都是类似的(打开资源、读取资源、关闭资源),因此如果能抽象出一个统一的接口来对这些底层资源进行统一访问,是不是很方便,而且使我们系统更加简洁,都是对不同的底层资源使用同一个接口进行访问。
Spring 提供一个Resource接口来统一这些底层资源一致的访问,而且提供了一些便利的接口,从而能提供我们的生产力。
代码
- Resource接口(Spring的Resource接口代表底层外部资源,提供了对底层外部资源的一致性访问接口)
// 资源输入流
public interface InputStreamSource {
// 获取资源输入流
InputStream getInputStream() throws IOException;
}
// 资源接口
public interface Resource extends InputStreamSource {
// 返回当前Resource代表的底层资源是否存在,true表示存在。
boolean exists();
// 返回当前Resource代表的底层资源是否可读,true表示可读。
default boolean isReadable() {
return this.exists();
}
// 返回当前Resource代表的底层资源是否已经打开,如果返回true,则只能被读取一次然后关闭以避免资源泄露;常见的Resource实现一般返回false。
default boolean isOpen() {
return false;
}
// 返回当前Resource代表的底层资源是否是文件
default boolean isFile() {
return false;
}
// 如果当前Resource代表的底层资源能由java.util.URL代表,则返回该URL,否则抛出IOException。
URL getURL() throws IOException;
// 如果当前Resource代表的底层资源能由java.util.URI代表,则返回该URI,否则抛出IOException。
URI getURI() throws IOException;
// 如果当前Resource代表的底层资源能由java.io.File代表,则返回该File,否则抛出IOException。
File getFile() throws IOException;
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(this.getInputStream());
}
// 返回当前Resource代表的底层文件资源的长度,一般是值代表的文件资源的长度。
long contentLength() throws IOException;
// 返回当前Resource代表的底层资源的最后修改时间。
long lastModified() throws IOException;
// 用于创建相对于当前Resource代表的底层资源的资源,比如当前Resource代表文件资源“d:/test/”则createRelative(“test.txt”)将返回表文件资源“d:/test/test.txt”Resource资源。
Resource createRelative(String var1) throws IOException;
// 返回当前Resource代表的底层文件资源的文件路径,比如File资源“file://d:/test.txt”将返回“d:/test.txt”,而URL资源http://www.javass.cn将返回“”,因为只返回文件路径。
@Nullable
String getFilename();
// 返回当前Resource代表的底层资源的描述符,通常就是资源的全路径(实际文件名或实际URL地址)。
String getDescription();
}
Resource接口提供了足够的抽象,足够满足我们日常使用。而且提供了很多内置Resource实现:ByteArrayResource、InputStreamResource 、FileSystemResource 、UrlResource 、ClassPathResource、ServletContextResource、VfsResource等。
- AbstractResource抽象类
// 资源抽象类
public abstract class AbstractResource implements Resource {
// 构造器
public AbstractResource() {
}
// 判断当前资源是否存在
public boolean exists() {
Log logger;
// 判断当前资源是否是文件
if (this.isFile()) {
try {
// 判断当前文件是否存在
return this.getFile().exists();
} catch (IOException var4) {
logger = LogFactory.getLog(this.getClass());
if (logger.isDebugEnabled()) {
logger.debug("Could not retrieve File for existence check of " + this.getDescription(), var4);
}
}
}
try {
// 关闭资源输入流
this.getInputStream().close();
return true;
} catch (Throwable var3) {
logger = LogFactory.getLog(this.getClass());
if (logger.isDebugEnabled()) {
logger.debug("Could not retrieve InputStream for existence check of " + this.getDescription(), var3);
}
return false;
}
}
// 是否可读
public boolean isReadable() {
// 当前文件存在则就可读
return this.exists();
}
// 资源是否以及打开
public boolean isOpen() {
// 默认是false
return false;
}
// 资源是否是文件
public boolean isFile() {
// 默认时候false
return false;
}
// 获取资源代表的URL
public URL getURL() throws IOException {
throw new FileNotFoundException(this.getDescription() + " cannot be resolved to URL");
}
// 获取资源代表的URI
public URI getURI() throws IOException {
// 获取URL
URL url = this.getURL();
try {
// 获取URL中的URI
return ResourceUtils.toURI(url);
} catch (URISyntaxException var3) {
throw new NestedIOException("Invalid URI [" + url + "]", var3);
}
}
// 获取资源代表的文件
public File getFile() throws IOException {
throw new FileNotFoundException(this.getDescription() + " cannot be resolved to absolute file path");
}
public ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(this.getInputStream());
}
// 获取文件资源长度
public long contentLength() throws IOException {
// 获取文件资源输入流
InputStream is = this.getInputStream();
boolean var16 = false;
// 记录文件长度的var6
long var6;
try {
var16 = true;
// 临时记录文件长度的size
long size = 0L;
// 字节缓冲区
byte[] buf = new byte[256];
// 循环读
while(true) {
int read;
// 如果文件中没有字节了
if ((read = is.read(buf)) == -1) {
// 将临时记录赋值给var6
var6 = size;
// var16赋值为false
var16 = false;
// 结束循环
break;
}
// 累计
size += (long)read;
}
} finally {
if (var16) {
try {
is.close();
} catch (IOException var17) {
Log logger = LogFactory.getLog(this.getClass());
if (logger.isDebugEnabled()) {
logger.debug("Could not close content-length InputStream for " + this.getDescription(), var17);
}
}
}
}
try {
is.close();
} catch (IOException var18) {
Log logger = LogFactory.getLog(this.getClass());
if (logger.isDebugEnabled()) {
logger.debug("Could not close content-length InputStream for " + this.getDescription(), var18);
}
}
return var6;
}
// 获取资源最后修改时间
public long lastModified() throws IOException {
// 获取当前资源文件
File fileToCheck = this.getFileForLastModifiedCheck();
// 获取资源文件最后修改时间
long lastModified = fileToCheck.lastModified();
// 如果最后修改时间等于0同时文件不存在
if (lastModified == 0L && !fileToCheck.exists()) {
throw new FileNotFoundException(this.getDescription() + " cannot be resolved in the file system for checking its last-modified timestamp");
} else {
return lastModified;
}
}
// 获取文件最后最改时间
protected File getFileForLastModifiedCheck() throws IOException {
return this.getFile();
}
public Resource createRelative(String relativePath) throws IOException {
throw new FileNotFoundException("Cannot create a relative resource for " + this.getDescription());
}
// 获取资源文件名称
@Nullable
public String getFilename() {
return null;
}
public boolean equals(@Nullable Object other) {
return this == other || other instanceof Resource && ((Resource)other).getDescription().equals(this.getDescription());
}
public int hashCode() {
return this.getDescription().hashCode();
}
public String toString() {
return this.getDescription();
}
}
- 具体实现类
1.ByteArrayResource实现类
// 字节数组资源
public class ByteArrayResource extends AbstractResource {
// 字节数组
private final byte[] byteArray;
// 描述
private final String description;
// 构造器(字节数组)
public ByteArrayResource(byte[] byteArray) {
this(byteArray, "resource loaded from byte array");
}
// 构造器(字节数组,描述)
public ByteArrayResource(byte[] byteArray, @Nullable String description) {
// 成员变量进行初始化
Assert.notNull(byteArray, "Byte array must not be null");
this.byteArray = byteArray;
this.description = description != null ? description : "";
}
// 获取字节数组
public final byte[] getByteArray() {
return this.byteArray;
}
// 判断是否为空(一般判断的是文件)
public boolean exists() {
// 默认是true
return true;
}
// 返回字节长度
public long contentLength() {
return (long)this.byteArray.length;
}
// 获取资源输入流
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(this.byteArray);
}
// 获取资源描述
public String getDescription() {
return "Byte array resource [" + this.description + "]";
}
public boolean equals(@Nullable Object other) {
return this == other || other instanceof ByteArrayResource && Arrays.equals(((ByteArrayResource)other).byteArray, this.byteArray);
}
public int hashCode() {
return byte[].class.hashCode() * 29 * this.byteArray.length;
}
}
2.FileSystemResource实现类
public class FileSystemResource extends AbstractResource implements WritableResource {
// 资源路径
private final String path;
// 资源文件
@Nullable
private final File file;
// 资源文件路径对象
private final Path filePath;
// 构造器(资源路径)
public FileSystemResource(String path) {
// 初始化成员变量
Assert.notNull(path, "Path must not be null");
this.path = StringUtils.cleanPath(path);
this.file = new File(path);
this.filePath = this.file.toPath();
}
// 构造器(资源文件对象)
public FileSystemResource(File file) {
// 初始化成员变量
Assert.notNull(file, "File must not be null");
this.path = StringUtils.cleanPath(file.getPath());
this.file = file;
this.filePath = file.toPath();
}
// 构造器(资源路径对象)
public FileSystemResource(Path filePath) {
// 初始化成员变量
Assert.notNull(filePath, "Path must not be null");
this.path = StringUtils.cleanPath(filePath.toString());
this.file = null;
this.filePath = filePath;
}
// 构造器(资源文件系统对象,资源路径)
public FileSystemResource(FileSystem fileSystem, String path) {
// 初始化成员变量
Assert.notNull(fileSystem, "FileSystem must not be null");
Assert.notNull(path, "Path must not be null");
this.path = StringUtils.cleanPath(path);
this.file = null;
this.filePath = fileSystem.getPath(this.path).normalize();
}
// 获取资源路径
public final String getPath() {
return this.path;
}
// 判断资源文件是否存在
public boolean exists() {
return this.file != null ? this.file.exists() : Files.exists(this.filePath, new LinkOption[0]);
}
// 判断资源文件是否可读
public boolean isReadable() {
return this.file != null ? this.file.canRead() && !this.file.isDirectory() : Files.isReadable(this.filePath) && !Files.isDirectory(this.filePath, new LinkOption[0]);
}
// 获取资源文件输入流
public InputStream getInputStream() throws IOException {
try {
return Files.newInputStream(this.filePath);
} catch (NoSuchFileException var2) {
throw new FileNotFoundException(var2.getMessage());
}
}
// 是否可写
public boolean isWritable() {
return this.file != null ? this.file.canWrite() && !this.file.isDirectory() : Files.isWritable(this.filePath) && !Files.isDirectory(this.filePath, new LinkOption[0]);
}
// 获取资源文件输出流
public OutputStream getOutputStream() throws IOException {
return Files.newOutputStream(this.filePath);
}
// 获取资源代表的URL
public URL getURL() throws IOException {
return this.file != null ? this.file.toURI().toURL() : this.filePath.toUri().toURL();
}
// 获取资源代表的URI
public URI getURI() throws IOException {
return this.file != null ? this.file.toURI() : this.filePath.toUri();
}
// 判断是否是文件
public boolean isFile() {
// 默认是true
return true;
}
// 获取资源文件
public File getFile() {
return this.file != null ? this.file : this.filePath.toFile();
}
// 获取读通道
public ReadableByteChannel readableChannel() throws IOException {
try {
return FileChannel.open(this.filePath, StandardOpenOption.READ);
} catch (NoSuchFileException var2) {
throw new FileNotFoundException(var2.getMessage());
}
}
// 获取写通道
public WritableByteChannel writableChannel() throws IOException {
return FileChannel.open(this.filePath, StandardOpenOption.WRITE);
}
// 获取文件长度
public long contentLength() throws IOException {
if (this.file != null) {
long length = this.file.length();
if (length == 0L && !this.file.exists()) {
throw new FileNotFoundException(this.getDescription() + " cannot be resolved in the file system for checking its content length");
} else {
return length;
}
} else {
try {
return Files.size(this.filePath);
} catch (NoSuchFileException var3) {
throw new FileNotFoundException(var3.getMessage());
}
}
}
// 获取文件最后修改时间
public long lastModified() throws IOException {
if (this.file != null) {
return super.lastModified();
} else {
try {
return Files.getLastModifiedTime(this.filePath).toMillis();
} catch (NoSuchFileException var2) {
throw new FileNotFoundException(var2.getMessage());
}
}
}
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
return this.file != null ? new FileSystemResource(pathToUse) : new FileSystemResource(this.filePath.getFileSystem(), pathToUse);
}
// 获取资源文件名称
public String getFilename() {
return this.file != null ? this.file.getName() : this.filePath.getFileName().toString();
}
// 获取资源文件描述
public String getDescription() {
return "file [" + (this.file != null ? this.file.getAbsolutePath() : this.filePath.toAbsolutePath()) + "]";
}
public boolean equals(@Nullable Object other) {
return this == other || other instanceof FileSystemResource && this.path.equals(((FileSystemResource)other).path);
}
public int hashCode() {
return this.path.hashCode();
}
}