org.springframework.core.io.ClassPathResource位于Spring核心core下,用以表达类路径下的资源。
首先简要说明一下什么是classpath,顾名思义,就是存放*.class类文件的路径,或者说ClassLoader加载类时为找到 *.class文件的路径。我们以一个WEB项目为例,发布后的目录结构大致如下:
然后以Tomcat为例,看一下WEB项目类加载时候的目录,参考 Tomcat Class Loader How-To 中的说明:
WebappX — A class loader is created for each web application that is deployed in a single Tomcat instance. All unpacked classes and resources in the /WEB-INF/classes directory of your web application, plus classes and resources in JAR files under the /WEB-INF/lib directory of your web application, are made visible to this web application, but not to other ones.
因此,对于部署在Tomcat上的WEB应用来说,/WEB-INF/classes和/WEB-INF/lib目录就是我们所指的classpath。
ClassPathResource是org.springframework.core.io.Resource接口的实现类。可以使用ClassLoader或Class类加载资源。支持转换为java.io.File对象(在Jar文件中的资源除外)。其继承实现关系图如下:
ClasspathResource类的属性变量和构造方法如下:
private final String path;
@Nullable
private ClassLoader classLoader;// 通过ClassLoader加载资源文件
@Nullable
private Class<?> clazz; // 通过Class类加载资源文件
// 通过类路径创建resource
public ClassPathResource(String path){...}
// 通过类路径和给定的ClassLoader创建resource
public ClassPathResource(String path, @Nullable ClassLoader classLoader){...}
// 通过类路径和给定的Class类创建resource
public ClassPathResource(String path, @Nullable Class<?> clazz){...}
// 通过类路径和给定的ClassLoader或Class创建resource
protected ClassPathResource(String path, @Nullable ClassLoader classLoader, @Nullable Class<?> clazz){...}
在类继承关系中,每个类定义的方法如下:
Resource | AbstractResource | AbstractFileResolvingResource | ClassPathResource |
---|---|---|---|
对底层资源的抽象描述 比如文件或类路径资源 |
对资源接口描述的基础实现 预先实现特定的行为 |
将URL解析为文件资源引用的抽象基类,尤其对JBOOS的vfs文件协议的支持 | 类路径资源的Resource实现 |
boolean exists() 判断该资源是否存在 |
public boolean exists() 实现父接口方法,检查资源(文件或目录)File对象或资源(文件)InputStream对象是否打开 |
public boolean exists() 重写父类方法,getURL后如果URL的文件协议是 file: 、vfsfile: 、vfs 则返回getFile().exists() 直接判断该文件是否存在,如果非上述文件协议则尝试判断是否网络资源,通过HTTP请求看是否返回HTTP Status-Code=200,如果扔非上述,则尝试getInputStream().close() 看文件流是否可打开 |
public boolean exists() 重写父类方法,判断是否能获取到该资源的URL对象 |
boolean isReadable() 是否可通过InputStreamSource.getInputStream()读取,这里注意返回true仍然可能读取失败,但返回false一定是不能读取 默认返回exists() |
public boolean isReadable() 实现父接口方法,该方法在当资源存在的情况下始终返回true,返回值与父接口方法中定义的默认返回值一致,即 返回exists() |
public boolean isReadable() 重写父类方法,getURL后如果URL的文件协议是 file: 、vfsfile: 、vfs 则getFile得到该资源File对象,判断该File资源是否非目录且canRead可读,如果非上述文件协议则尝试调用网络资源,判断是否可成功调用且返回内容长度大于0,如果扔非上述,则尝试getInputStream().close() 看文件流是否可打开 |
|
boolean isOpen() 表明该资源是否有打开的stream流,如果返回true则InputStream无法多次读取,且读完之后关闭流以防止内存泄露 默认返回false |
public boolean isOpen() 实现父接口方法,与接口方法中定义的默认返回值一致,即始终 返回false |
||
boolean isFile() 判断该文件是否是系统文件中的文件,true值表示(但不保证)可以成功调用getFile()方法 默认返回false |
public boolean isFile() 实现父接口方法,与接口方法中定义的默认返回值一致,即始终 返回false |
public boolean isFile() 重写父类方法,getURL后如果url为vfs开头协议(vfs/vfsfile)则交给 VfsResourceDelegate 类判断,如果url为file协议则返回true |
|
protected boolean isFile(URI uri) 重载isFile方法,根据指定的URI判断该资源是否是一个文件引用,如果URI的scheme以vfs开头则交给 VfsResourceDelegate 类判断,否则判断如果URI的scheme等于file 则返回true |
|||
URL getURL() 返回该资源对应的URL |
public URL getURL() 实现父接口方法,这里假设资源不能解析为URL,直接返回了 FileNotFoundException 异常 |
public URL getURL() 重写父类方法,根据类路径参数获取该资源的URL对象 |
|
URI getURI() 返回该资源对应的URI |
public URI getURI() 实现父接口方法,基于getURL返回的URL构建一个URI |
||
File getFile() 返回该资源的File对象 |
public File getFile() 实现父接口方法,这里假设资源无法解析为文件绝对路径,直接返回了 FileNotFoundException 异常 |
public File getFile() 重写父类方法,父类直接返回 FileNotFoundException 异常,在这里通过getURL获取到URL对象(具体URL由其子类确定,比如ClasspathResource重写了getURL通过类加载器获取到了类路径资源的URL),如果url为vfs开头协议(vfs/vfsfile)则交给VfsResourceDelegate 类获取File对象,否则通过得到url获取File对象 |
|
protected File getFile(URI uri) 重载getFile方法,根据指定的URI获取资源File对象,如果URI的scheme以vfs开头则交给 VfsResourceDelegate 类获取 |
|||
ReadableByteChannel readableChannel()默认返回Channels.newChannel(getInputStream()) |
public ReadableByteChannel readableChannel() 实现父接口方法,与父接口的默认返回值相同 |
public ReadableByteChannel readableChannel() 根据指定的URI调用 FileChannel.open 返回一个ReadableByteChannel 对象 |
|
long contentLength() 返回该资源内容的长度 |
public long contentLength() 根据 getInputStream() 返回的InputStream,读取并计算资源的内容长度 |
public long contentLength() 根据getURL获得URL对象,如果是文件(file/vfsfile/vfs)URL返回文件长度,否则尝试网络连接获取资源并返回长度 |
|
long lastModified() 返回该资源最后一次修改的时间戳 |
public long lastModified() 获取并返回该资源文件的时间戳 |
public long lastModified() 根据getURL获取URL对象,如果是文件(file/vfsfile/vfs)或归档文件(jar/war/zip/vfszip/wsjar)则获取文件的最后修改时间戳,否则获取网络连接并获取最后修改时间戳 |
|
Resource createRelative(String relativePath) 根据相对于该资源的相对路径,创建一个Resource资源,比如classpath资源目录conf下有A.xml和B.xml, Resource a = new ClassPathResource("conf/A.xml"); 那么在创建b资源的时候就可以以a为参照Resource b = a.createRelative("B.xml"); |
public Resource createRelative(String relativePath) 实现父接口方法,这里假设改相对资源未被创建,直接返回了 FileNotFoundException 异常 |
public Resource createRelative(String relativePath) 重写父类方法,根据参照资源的类路径得到relativePath参数的真实classpath路径,并创建Resource资源对象 |
|
String getFilename() 返回该资源的文件名,通常是路径的最后一部分,比如:myfile.txt |
public String getFilename() 实现父接口方法,这里假设该资源无文件名,直接返回了 null |
public String getFilename() 重写父类方法,根据classpath截取后面的文件名并返回 |
|
String getDescription() 返回该资源的描述,用于该资源在处理时的错误输出 |
public String getDescription() 重写父类方法,返回格式如 class path resource [...] 的内容 |
||
protected File getFileForLastModifiedCheck() 获取用于时间戳检查的文件,这里默认返回了getFile() |
protected File getFileForLastModifiedCheck() 重写父类方法,扩展了父类方法,在getFile之前先判断url协议是否为 jar/war/zip/vfszip/wsjar ,如果是则获取最外层的文件URL对应的File对象,比如嵌套在war中的jar文件,则返回war文件File对象。这里判断vfs开头协议扔交给VfsResourceDelegate 类来处理 |
||
public boolean equals(Object other) 重写Object的equals方法,用于比较两个Resource的Description是否相同 |
public boolean equals(Object other) 重写Object方法,比较类路径的值是否相同 |
||
public int hashCode() 重写Object的hashCode方法,用于获取Resource的Description值的hashCode值 |
public int hashCode() 重写Object方法,获取类路径classpath的hashCode值 |
||
public String toString() 重写Object的toString方法,返回Resource的Description信息 |
|||
public final String getPath() 返回该资源的classpath,构造函数的path参数经过规范化处理的结果 |
|||
public final ClassLoader getClassLoader() 如果指定了Class,则通过该Class获取ClassLoader,否则返回属性变量的ClassLoader参数 |
|||
public InputStream getInputStream() Resource继承了InputStreamSource接口,在ClassPathResource中得到了具体的实现,根据资源路径得到文件流 |
/**
* Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime.
*/
private static class VfsResourceDelegate {
public static Resource getResource(URL url) throws IOException {
return new VfsResource(VfsUtils.getRoot(url));
}
public static Resource getResource(URI uri) throws IOException {
return new VfsResource(VfsUtils.getRoot(uri));
}
}
ClassPathResource的使用:
Resource resource = new ClassPathResource("conf/custom-beans.xml");
参数path应在类路径下能够被ClassLoader所加载。
获取到了Resource对象也就等于获取到了该资源文件,后面可以根据方法的定义对文件进行相关操作。
System.out.println(resource.getURL());
System.out.println(resource.getFilename());
System.out.println(resource.getFile().getPath());
// ... ....