JDK操縱底層資源基本就是 java.net.URL 、java.io.File 、java.util.Properties這些。取資源基本是根據絕對路徑或當前類的相對路徑來取。從類路徑或Web容器上下文中獲取資源的時候也不方便。Resource接口提供了更強大的訪問底層資源的能力。
廢話不多說,看源碼之前先來看一下Resource的類結構。
一、類結構
一、Resource接口
如圖,Resouce接口並不是一個根接口,它繼承了一個簡單的父接口 InputStreamSource,這個接口只有一個方法,用以返回一個輸入流:
InputStream getInputStream() throws IOException;
來,直接上Resource接口的源碼,中文是我根據英文注釋自己翻譯的,如下:
public interface Resource extends InputStreamSource { boolean exists(); // 資源是否存在
boolean isReadable(); // 資源是否可讀
boolean isOpen(); // 資源所代表的句柄是否被一個stream打開了 URL getURL() throws IOException; // 返回資源的URL的句柄 URI getURI() throws IOException; // 返回資源的URI的句柄 File getFile() throws IOException; // 返回資源的File的句柄
long contentLength() throws IOException; // 資源內容的長度
long lastModified() throws IOException; // 資源最后的修改時間 Resource createRelative(String relativePath) throws IOException; //根據資源的相對路徑創建新資源 String getFilename(); // 資源的文件名 String getDescription(); //資源的描述 }
這個沒什么好說的,繼續!
二、抽象類AbstractResource
對於任何的接口而言,這個直接抽象類是重中之重,里面濃縮了接口的大部分公共實現。翻譯后如下:
結論:
1、增加了一個方法,protected File getFileForLastModifiedCheck() throws IOException,要求子類實現,如果子類未實現,那么直接返回資源文件。這個方法的具體作用,后面再看實現類。
2、方法 contentLength() ,是一個很比較重量級的方法,它通過將資源全部讀取一遍來判斷資源的字節數。255字節的緩沖數組來讀取。子類一般會重寫。(調整一下緩沖數組的大小?)
3、getDescription() 是這個抽象類唯一沒有實現的接口方法,留給子類去實現,資源文件默認的equals()、hashCode() 都通過這個來判斷。
4、InputStreamSource這個祖先接口的唯一方法 getInputStream()也沒有被實現,留給子類。
三、Resource的子接口ContextResource和WritableResource
這兩個接口繼承於Resource,擁有Resource的全部方法。其中,ContextResource接口增加了一個方法:
String getPathWithinContext(); // 返回上下文內的路徑
這個方法使得它的實現類有了返回當前上下文路徑的能力。
WritableResource接口增加了2個方法:
boolean isWritable(); // 是否可寫 OutputStream getOutputStream() throws IOException; //返回資源的寫入流
這個方法使得它的實現類擁有了寫資源的能力。
四、重要的抽象類AbstractFileResolvingResource
這個抽象類繼承自AbstractResource,重寫了AbstractResource的大部分方法。
public abstract class AbstractFileResolvingResource extends AbstractResource {
@Override
public File getFile() throws IOException { // 通過資源的URL得到資源本身,是文件就返回文件,否則返回描述
URL url = getURL();
if (url.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
return VfsResourceDelegate.getResource(url).getFile();
}
return ResourceUtils.getFile(url, getDescription());
}
@Override
protected File getFileForLastModifiedCheck() throws IOException { //從<壓縮文件地址>中獲取文件
URL url = getURL();
if (ResourceUtils.isJarURL(url)) {
URL actualUrl = ResourceUtils.extractJarFileURL(url);
if (actualUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
return VfsResourceDelegate.getResource(actualUrl).getFile();
}
return ResourceUtils.getFile(actualUrl, "Jar URL");
}
else {
return getFile();
}
}
protected File getFile(URI uri) throws IOException { // 通過資源uri獲取文件
if (uri.getScheme().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
return VfsResourceDelegate.getResource(uri).getFile();
}
return ResourceUtils.getFile(uri, getDescription());
}
@Override
public boolean exists() { //判斷資源是否存在,如果是文件Url,直接獲取文件判斷,否則,建立連接來判斷。
try {
URL url = getURL();
if (ResourceUtils.isFileURL(url)) {
// Proceed with file system resolution...
return getFile().exists();
}
else {
// Try a URL connection content-length header...
URLConnection con = url.openConnection();
ResourceUtils.useCachesIfNecessary(con);
HttpURLConnection httpCon =
(con instanceof HttpURLConnection ? (HttpURLConnection) con : null);
if (httpCon != null) {
httpCon.setRequestMethod("HEAD");
int code = httpCon.getResponseCode();
if (code == HttpURLConnection.HTTP_OK) {
return true;
}
else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
return false;
}
}
if (con.getContentLength() >= 0) {
return true;
}
if (httpCon != null) {
// no HTTP OK status, and no content-length header: give up
httpCon.disconnect();
return false;
}
else {
// Fall back to stream existence: can we open the stream?
InputStream is = getInputStream();
is.close();
return true;
}
}
}
catch (IOException ex) {
return false;
}
}
@Override
public boolean isReadable() { // 是否可讀
try {
URL url = getURL();
if (ResourceUtils.isFileURL(url)) {
// Proceed with file system resolution...
File file = getFile();
return (file.canRead() && !file.isDirectory());
}
else {
return true;
}
}
catch (IOException ex) {
return false;
}
}
@Override
public long contentLength() throws IOException {
URL url = getURL();
if (ResourceUtils.isFileURL(url)) {
// Proceed with file system resolution...
return getFile().length();
}
else {
// Try a URL connection content-length header...
URLConnection con = url.openConnection();
ResourceUtils.useCachesIfNecessary(con);
if (con instanceof HttpURLConnection) {
((HttpURLConnection) con).setRequestMethod("HEAD");
}
return con.getContentLength();
}
}
@Override
public long lastModified() throws IOException {
URL url = getURL();
if (ResourceUtils.isFileURL(url) || ResourceUtils.isJarURL(url)) {
// Proceed with file system resolution...
return super.lastModified();
}
else {
// Try a URL connection last-modified header...
URLConnection con = url.openConnection();
ResourceUtils.useCachesIfNecessary(con);
if (con instanceof HttpURLConnection) {
((HttpURLConnection) con).setRequestMethod("HEAD");
}
return con.getLastModified();
}
}
/**
* 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));
}
}
}
這個抽象類的子類都需要重寫繼承自AbstractResource的getURL()方法。因為絕大多數方法都依賴這個方法,進行資源在url上的操作。
所以在查看資源情況的時候,需要根據url建立連接來查看。

