目录
概述
Resource 是 Spring框架中统一管理底层资源的接口。
1、ClassPathResource
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.core.io;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
public class ClassPathResource extends AbstractFileResolvingResource {
// 文件路径,例如 application-context.xml文件
private final String path;
// 类加载器
@Nullable
private ClassLoader classLoader;
@Nullable
private Class<?> clazz;
public ClassPathResource(String path) {
this(path, (ClassLoader)null);
}
// 构造函数
public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
String pathToUse = StringUtils.cleanPath(path);
if(pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
// 初始化文件路径
this.path = pathToUse;
// 指定类加载器,如果没有传类加载器,则用默认的类加载器,翻看源码是:Thread.currentThread().getContextClassLoader()
this.classLoader = classLoader != null?classLoader:ClassUtils.getDefaultClassLoader();
}
public ClassPathResource(String path, @Nullable Class<?> clazz) {
Assert.notNull(path, "Path must not be null");
this.path = StringUtils.cleanPath(path);
this.clazz = clazz;
}
/** @deprecated */
@Deprecated
protected ClassPathResource(String path, @Nullable ClassLoader classLoader, @Nullable Class<?> clazz) {
this.path = StringUtils.cleanPath(path);
this.classLoader = classLoader;
this.clazz = clazz;
}
// 获取配置文件路径
public final String getPath() {
return this.path;
}
// 获取类加载器
@Nullable
public final ClassLoader getClassLoader() {
return this.clazz != null?this.clazz.getClassLoader():this.classLoader;
}
// 判断文件是否存在
public boolean exists() {
return this.resolveURL() != null;
}
@Nullable
protected URL resolveURL() {
return this.clazz != null?this.clazz.getResource(this.path):(this.classLoader != null?this.classLoader.getResource(this.path):ClassLoader.getSystemResource(this.path));
}
// 此方法继承自InputStreamSource,ClassPathResource实现了具体逻辑
// 主要就是通过类加载器加载配置文件到内存 InputStream中
public InputStream getInputStream() throws IOException {
InputStream is;
if(this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
} else if(this.classLoader != null) {
is = this.classLoader.getResourceAsStream(this.path);
} else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if(is == null) {
throw new FileNotFoundException(this.getDescription() + " cannot be opened because it does not exist");
} else {
return is;
}
}
public URL getURL() throws IOException {
URL url = this.resolveURL();
if(url == null) {
throw new FileNotFoundException(this.getDescription() + " cannot be resolved to URL because it does not exist");
} else {
return url;
}
}
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
return this.clazz != null?new ClassPathResource(pathToUse, this.clazz):new ClassPathResource(pathToUse, this.classLoader);
}
@Nullable
public String getFilename() {
return StringUtils.getFilename(this.path);
}
public String getDescription() {
StringBuilder builder = new StringBuilder("class path resource [");
String pathToUse = this.path;
if(this.clazz != null && !pathToUse.startsWith("/")) {
builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));
builder.append('/');
}
if(pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
builder.append(pathToUse);
builder.append(']');
return builder.toString();
}
public boolean equals(@Nullable Object other) {
if(this == other) {
return true;
} else if(!(other instanceof ClassPathResource)) {
return false;
} else {
ClassPathResource otherRes = (ClassPathResource)other;
return this.path.equals(otherRes.path) && ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz);
}
}
public int hashCode() {
return this.path.hashCode();
}
}
如以上代码中注释的,ClassPathResource 初始化时主要保存 classpath路径下配置文件路径,选择类加载器,然后在继承自 InputStreamResource 的方法 getInputStream中 通过 类加载器加载配置文件到内存(InputStream)中,供Reader使用。
2、FileSystemResource
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.core.io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
public class FileSystemResource extends AbstractResource implements WritableResource {
// 资源路径
private final String path;
// 资源路径File
@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, new String[0]).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]);
}
// 继承自 InputStreamResource,加载并获取配置文件的流
public InputStream getInputStream() throws IOException {
try {
// 之前版本是直接用 FileInputStream(this.file);5.0版本变成NIO方式加载,待研究
return Files.newInputStream(this.filePath, new OpenOption[0]);
} 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, new OpenOption[0]);
}
public URL getURL() throws IOException {
return this.file != null?this.file.toURI().toURL():this.filePath.toUri().toURL();
}
public URI getURI() throws IOException {
return this.file != null?this.file.toURI():this.filePath.toUri();
}
public boolean isFile() {
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, new OpenOption[]{StandardOpenOption.READ});
} catch (NoSuchFileException var2) {
throw new FileNotFoundException(var2.getMessage());
}
}
public WritableByteChannel writableChannel() throws IOException {
return FileChannel.open(this.filePath, new OpenOption[]{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, new LinkOption[0]).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();
}
}