一、文件系统资源 FileSystemResource
文件系统资源 FileSystemResource,资源以文件系统路径的方式表示,唯一一个实现了WritableResource接口的类。这个类由2个不可变的属性 file 和 path ,本质上就是一个java.io.File 的包装。这个类的 equals() 和 hashcode() 都通过属性 path 来操作。
public class FileSystemResource extends AbstractResource implements WritableResource {
private final File file; // 不可变属性
private final String path; // 不可变属性
public FileSystemResource(File file) { // 简单的构造方法,path为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 { //InputStreamSource接口的实现方法
return new FileInputStream(this.file);
}
@Override
public URL getURL() throws IOException { //可见这个url是通过uri得到的
return this.file.toURI().toURL();
}
@Override
public URI getURI() throws IOException {
return this.file.toURI();
}
@Override
public File getFile() {
return this.file;
}
@Override
public long contentLength() throws IOException {
return this.file.length();
}
@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();
}
public String getDescription() { // 资源描述,直接用绝对路径来构造
return "file [" + this.file.getAbsolutePath() + "]";
}
public boolean isWritable() { // WritableResource接口的实现方法
return (this.file.canWrite() && !this.file.isDirectory());
}
public OutputStream getOutputStream() throws IOException {
return new FileOutputStream(this.file);
}
@Override
public boolean equals(Object obj) { //通过path来比较
return (obj == this ||
(obj instanceof FileSystemResource && this.path.equals(((FileSystemResource) obj).path)));
}
@Override
public int hashCode() { // 文件资源的HashCode就是path的hashCode
return this.path.hashCode();
}
}
二、常用的ClassPathResource
ClassPathResource这个资源类表示的是类路径下的资源,资源以相对于类路径的方式表示,是基于class的 getResourceAsStream(this.path) 或者 this.classLoader.getResourceAsStream(this.path) 。
public class ClassPathResource extends AbstractFileResolvingResource {
private final String path;
private ClassLoader classLoader;
private Class<?> clazz;
/**
* Create a new ClassPathResource for ClassLoader usage.
* A leading slash will be removed, as the ClassLoader
* resource access methods will not accept it.
* <p>The thread context class loader will be used for
* loading the resource.
* @param path the absolute path within the class path
* @see java.lang.ClassLoader#getResourceAsStream(String)
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
*/
public ClassPathResource(String path) {
this(path, (ClassLoader) null);
}
/**
* Create a new ClassPathResource for ClassLoader usage.
* A leading slash will be removed, as the ClassLoader
* resource access methods will not accept it.
* @param path the absolute path within the classpath
* @param classLoader the class loader to load the resource with,
* or {@code null} for the thread context class loader
* @see ClassLoader#getResourceAsStream(String)
*/
public ClassPathResource(String path, 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;
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
/**
* Create a new ClassPathResource for Class usage.
* The path can be relative to the given class,
* or absolute within the classpath via a leading slash.
* @param path relative or absolute path within the class path
* @param clazz the class to load resources with
* @see java.lang.Class#getResourceAsStream
*/
public ClassPathResource(String path, Class<?> clazz) {
Assert.notNull(path, "Path must not be null");
this.path = StringUtils.cleanPath(path);
this.clazz = clazz;
}
/**
* Create a new ClassPathResource with optional ClassLoader and Class.
* Only for internal usage.
* @param path relative or absolute path within the classpath
* @param classLoader the class loader to load the resource with, if any
* @param clazz the class to load resources with, if any
*/
protected ClassPathResource(String path, ClassLoader classLoader, Class<?> clazz) {
this.path = StringUtils.cleanPath(path);
this.classLoader = classLoader;
this.clazz = clazz;
}
/**
* Return the path for this resource (as resource path within the class path).
*/
public final String getPath() {
return this.path;
}
/**
* Return the ClassLoader that this resource will be obtained from.
*/
public final ClassLoader getClassLoader() {
return (this.classLoader != null ? this.classLoader : this.clazz.getClassLoader());
}
/**
* This implementation checks for the resolution of a resource URL.
* @see java.lang.ClassLoader#getResource(String)
* @see java.lang.Class#getResource(String)
*/
@Override
public boolean exists() {
URL url;
if (this.clazz != null) {
url = this.clazz.getResource(this.path);
}
else {
url = this.classLoader.getResource(this.path);
}
return (url != null);
}
/**
* This implementation opens an InputStream for the given class path resource.
* @see java.lang.ClassLoader#getResourceAsStream(String)
* @see java.lang.Class#getResourceAsStream(String)
*/
public InputStream getInputStream() throws IOException {
InputStream is;
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
}
else {
is = this.classLoader.getResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
return is;
}
/**
* This implementation returns a URL for the underlying class path resource.
* @see java.lang.ClassLoader#getResource(String)
* @see java.lang.Class#getResource(String)
*/
@Override
public URL getURL() throws IOException {
URL url;
if (this.clazz != null) {
url = this.clazz.getResource(this.path);
}
else {
url = this.classLoader.getResource(this.path);
}
if (url == null) {
throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");
}
return url;
}
/**
* This implementation creates a ClassPathResource, applying the given path
* relative to the path of the underlying resource of this descriptor.
* @see org.springframework.util.StringUtils#applyRelativePath(String, String)
*/
@Override
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
return new ClassPathResource(pathToUse, this.classLoader, this.clazz);
}
/**
* This implementation returns the name of the file that this class path
* resource refers to.
* @see org.springframework.util.StringUtils#getFilename(String)
*/
@Override
public String getFilename() {
return StringUtils.getFilename(this.path);
}
/**
* This implementation returns a description that includes the class path location.
*/
public String getDescription() {
StringBuilder builder = new StringBuilder("class path resource [");
String pathToUse = 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();
}
/**
* This implementation compares the underlying class path locations.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof ClassPathResource) {
ClassPathResource otherRes = (ClassPathResource) obj;
return (this.path.equals(otherRes.path) &&
ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) &&
ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz));
}
return false;
}
/**
* This implementation returns the hash code of the underlying
* class path location.
*/
@Override
public int hashCode() {
return this.path.hashCode();
}
}
三、Url资源——UrlResource
UrlResource这个资源类封装了可以以URL表示的各种资源。这个资源类有3个属性,一个URI、一个URL,以及一个规范化后的URL,用于资源间的比较以及计算HashCode。
public class UrlResource extends AbstractFileResolvingResource {
/**
* Original URI, if available; used for URI and File access.
*/
private final URI uri;
/**
* Original URL, used for actual access.
*/
private final URL url;
/**
* Cleaned URL (with normalized path), used for comparisons.
*/
private final URL cleanedUrl;
/**
* Create a new UrlResource based on the given URI object.
* @param uri a URI
* @throws MalformedURLException if the given URL path is not valid
*/
public UrlResource(URI uri) throws MalformedURLException {
Assert.notNull(uri, "URI must not be null");
this.uri = uri;
this.url = uri.toURL();
this.cleanedUrl = getCleanedUrl(this.url, uri.toString());
}
/**
* Create a new UrlResource based on the given URL object.
* @param url a URL
*/
public UrlResource(URL url) {
Assert.notNull(url, "URL must not be null");
this.url = url;
this.cleanedUrl = getCleanedUrl(this.url, url.toString());
this.uri = null;
}
/**
* Create a new UrlResource based on a URL path.
* <p>Note: The given path needs to be pre-encoded if necessary.
* @param path a URL path
* @throws MalformedURLException if the given URL path is not valid
* @see java.net.URL#URL(String)
*/
public UrlResource(String path) throws MalformedURLException {
Assert.notNull(path, "Path must not be null");
this.uri = null;
this.url = new URL(path);
this.cleanedUrl = getCleanedUrl(this.url, path);
}
/**
* Create a new UrlResource based on a URI specification.
* <p>The given parts will automatically get encoded if necessary.
* @param protocol the URL protocol to use (e.g. "jar" or "file" - without colon);
* also known as "scheme"
* @param location the location (e.g. the file path within that protocol);
* also known as "scheme-specific part"
* @throws MalformedURLException if the given URL specification is not valid
* @see java.net.URI#URI(String, String, String)
*/
public UrlResource(String protocol, String location) throws MalformedURLException {
this(protocol, location, null);
}
/**
* Create a new UrlResource based on a URI specification.
* <p>The given parts will automatically get encoded if necessary.
* @param protocol the URL protocol to use (e.g. "jar" or "file" - without colon);
* also known as "scheme"
* @param location the location (e.g. the file path within that protocol);
* also known as "scheme-specific part"
* @param fragment the fragment within that location (e.g. anchor on an HTML page,
* as following after a "#" separator)
* @throws MalformedURLException if the given URL specification is not valid
* @see java.net.URI#URI(String, String, String)
*/
public UrlResource(String protocol, String location, String fragment) throws MalformedURLException {
try {
this.uri = new URI(protocol, location, fragment);
this.url = this.uri.toURL();
this.cleanedUrl = getCleanedUrl(this.url, this.uri.toString());
}
catch (URISyntaxException ex) {
MalformedURLException exToThrow = new MalformedURLException(ex.getMessage());
exToThrow.initCause(ex);
throw exToThrow;
}
}
/**
* Determine a cleaned URL for the given original URL.
* @param originalUrl the original URL
* @param originalPath the original URL path
* @return the cleaned URL
* @see org.springframework.util.StringUtils#cleanPath
*/
private URL getCleanedUrl(URL originalUrl, String originalPath) {
try {
return new URL(StringUtils.cleanPath(originalPath));
}
catch (MalformedURLException ex) {
// Cleaned URL path cannot be converted to URL
// -> take original URL.
return originalUrl;
}
}
/**
* This implementation opens an InputStream for the given URL.
* It sets the "UseCaches" flag to {@code false},
* mainly to avoid jar file locking on Windows.
* @see java.net.URL#openConnection()
* @see java.net.URLConnection#setUseCaches(boolean)
* @see java.net.URLConnection#getInputStream()
*/
public InputStream getInputStream() throws IOException {
URLConnection con = this.url.openConnection();
ResourceUtils.useCachesIfNecessary(con);
try {
return con.getInputStream();
}
catch (IOException ex) {
// Close the HTTP connection (if applicable).
if (con instanceof HttpURLConnection) {
((HttpURLConnection) con).disconnect();
}
throw ex;
}
}
/**
* This implementation returns the underlying URL reference.
*/
@Override
public URL getURL() throws IOException {
return this.url;
}
/**
* This implementation returns the underlying URI directly,
* if possible.
*/
@Override
public URI getURI() throws IOException {
if (this.uri != null) {
return this.uri;
}
else {
return super.getURI();
}
}
/**
* This implementation returns a File reference for the underlying URL/URI,
* provided that it refers to a file in the file system.
* @see org.springframework.util.ResourceUtils#getFile(java.net.URL, String)
*/
@Override
public File getFile() throws IOException {
if (this.uri != null) {
return super.getFile(this.uri);
}
else {
return super.getFile();
}
}
/**
* This implementation creates a UrlResource, applying the given path
* relative to the path of the underlying URL of this resource descriptor.
* @see java.net.URL#URL(java.net.URL, String)
*/
@Override
public Resource createRelative(String relativePath) throws MalformedURLException {
if (relativePath.startsWith("/")) {
relativePath = relativePath.substring(1);
}
return new UrlResource(new URL(this.url, relativePath));
}
/**
* This implementation returns the name of the file that this URL refers to.
* @see java.net.URL#getFile()
* @see java.io.File#getName()
*/
@Override
public String getFilename() {
return new File(this.url.getFile()).getName();
}
/**
* This implementation returns a description that includes the URL.
*/
public String getDescription() {
return "URL [" + this.url + "]";
}
/**
* This implementation compares the underlying URL references.
*/
@Override
public boolean equals(Object obj) {
return (obj == this ||
(obj instanceof UrlResource && this.cleanedUrl.equals(((UrlResource) obj).cleanedUrl)));
}
/**
* This implementation returns the hash code of the underlying URL reference.
*/
@Override
public int hashCode() {
return this.cleanedUrl.hashCode();
}
}
四、Servlet上下文资源——ServletContextResource
实现基本就是基于 this.servletContext.getResource(this.path) 或 this.servletContext.getResourceAsStream(this.path) 这两个方法。
public class ServletContextResource extends AbstractFileResolvingResource implements ContextResource {
private final ServletContext servletContext;
private final String path;
/**
* Create a new ServletContextResource.
* <p>The Servlet spec requires that resource paths start with a slash,
* even if many containers accept paths without leading slash too.
* Consequently, the given path will be prepended with a slash if it
* doesn't already start with one.
* @param servletContext the ServletContext to load from
* @param path the path of the resource
*/
public ServletContextResource(ServletContext servletContext, String path) {
// check ServletContext
Assert.notNull(servletContext, "Cannot resolve ServletContextResource without ServletContext");
this.servletContext = servletContext;
// check path
Assert.notNull(path, "Path is required");
String pathToUse = StringUtils.cleanPath(path);
if (!pathToUse.startsWith("/")) {
pathToUse = "/" + pathToUse;
}
this.path = pathToUse;
}
/**
* Return the ServletContext for this resource.
*/
public final ServletContext getServletContext() {
return this.servletContext;
}
/**
* Return the path for this resource.
*/
public final String getPath() {
return this.path;
}
/**
* This implementation checks {@code ServletContext.getResource}.
* @see javax.servlet.ServletContext#getResource(String)
*/
@Override
public boolean exists() {
try {
URL url = this.servletContext.getResource(this.path);
return (url != null);
}
catch (MalformedURLException ex) {
return false;
}
}
/**
* This implementation delegates to {@code ServletContext.getResourceAsStream},
* which returns {@code null} in case of a non-readable resource (e.g. a directory).
* @see javax.servlet.ServletContext#getResourceAsStream(String)
*/
@Override
public boolean isReadable() {
InputStream is = this.servletContext.getResourceAsStream(this.path);
if (is != null) {
try {
is.close();
}
catch (IOException ex) {
// ignore
}
return true;
}
else {
return false;
}
}
/**
* This implementation delegates to {@code ServletContext.getResourceAsStream},
* but throws a FileNotFoundException if no resource found.
* @see javax.servlet.ServletContext#getResourceAsStream(String)
*/
public InputStream getInputStream() throws IOException {
InputStream is = this.servletContext.getResourceAsStream(this.path);
if (is == null) {
throw new FileNotFoundException("Could not open " + getDescription());
}
return is;
}
/**
* This implementation delegates to {@code ServletContext.getResource},
* but throws a FileNotFoundException if no resource found.
* @see javax.servlet.ServletContext#getResource(String)
*/
@Override
public URL getURL() throws IOException {
URL url = this.servletContext.getResource(this.path);
if (url == null) {
throw new FileNotFoundException(
getDescription() + " cannot be resolved to URL because it does not exist");
}
return url;
}
/**
* This implementation resolves "file:" URLs or alternatively delegates to
* {@code ServletContext.getRealPath}, throwing a FileNotFoundException
* if not found or not resolvable.
* @see javax.servlet.ServletContext#getResource(String)
* @see javax.servlet.ServletContext#getRealPath(String)
*/
@Override
public File getFile() throws IOException {
URL url = this.servletContext.getResource(this.path);
if (url != null && ResourceUtils.isFileURL(url)) {
// Proceed with file system resolution...
return super.getFile();
}
else {
String realPath = WebUtils.getRealPath(this.servletContext, this.path);
return new File(realPath);
}
}
/**
* This implementation creates a ServletContextResource, 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 ServletContextResource(this.servletContext, pathToUse);
}
/**
* This implementation returns the name of the file that this ServletContext
* resource refers to.
* @see org.springframework.util.StringUtils#getFilename(String)
*/
@Override
public String getFilename() {
return StringUtils.getFilename(this.path);
}
/**
* This implementation returns a description that includes the ServletContext
* resource location.
*/
public String getDescription() {
return "ServletContext resource [" + this.path + "]";
}
public String getPathWithinContext() {
return this.path;
}
/**
* This implementation compares the underlying ServletContext resource locations.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof ServletContextResource) {
ServletContextResource otherRes = (ServletContextResource) obj;
return (this.servletContext.equals(otherRes.servletContext) && this.path.equals(otherRes.path));
}
return false;
}
/**
* This implementation returns the hash code of the underlying
* ServletContext resource location.
*/
@Override
public int hashCode() {
return this.path.hashCode();
}
}
五、其它不常用的实现类
1、字节数组资源——ByteArrayResource
若需要操作描述一个字节数组,可以用这个资源类。ByteArrayResource可多次读取数组资源。
2、描述性资源——DescriptiveResource
若一个资源,仅仅有一个描述,非常抽象的这种情况,可以用这个资源类,它并没有指向一个实际的可读的资源。
3、输入流资源——InputStreamResource
输入流资源InputStreamResource,是一个不可变InputStream的包装和一个不可变的描述字符串。此外还有一个私有成员变量Boolean read用于限制本资源的InputStream不可被重复获取。这个包装类指向的是一个已经打开的资源,所以它的 isOpen()总是返回true。而且它不能重复获取资源,只能读取一次
4、VFS资源——VfsResource
vfs是Virtual File System虚拟文件系统,也称为虚拟文件系统开关(Virtual Filesystem Switch).是Linux档案系统对外的接口。任何要使用档案系统的程序都必须经由这层接口来使用它。(摘自百度百科...)它能一致的访问物理文件系统、jar资源、zip资源、war资源等,VFS能把这些资源一致的映射到一个目录上,访问它们就像访问物理文件资源一样,而其实这些资源不存在于物理文件系统。
5、Portlet上下文资源——PortletContextResource
Portlet是基于java的web组件,由portlet容器管理,并由容器处理请求,生产动态内容。这个资源类封装了一个不可变的javax.portlet.PortletContext对象和一个不可变的String对象代表路径。类中所有操作都基于这两个属性。PortletContextResource对象实现了ContextResource接口,实现了方法String getPathWithinContext(),即返回自身的path属性。