Spring資源抽象Resource


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

  對於任何的接口而言,這個直接抽象類是重中之重,里面濃縮了接口的大部分公共實現。翻譯后如下:

復制代碼
 
         
 
         
public abstract class AbstractResource implements Resource {
 
    public boolean exists() {  //判斷文件是否存在,若判斷過程產生異常(因為會調用SecurityManager來判斷),就關閉對應的流
        try {
            return getFile().exists();
        }
        catch (IOException ex) {
            try {
                InputStream is = getInputStream();  //getInputStream()方法會被子類重寫,
                is.close();
                return true;
            }
            catch (Throwable isEx) {
                return false;   
            }
        }
    }
 
    public boolean isReadable() {  //  直接返回true,可讀
        return true;
    }
 
    public boolean isOpen() {  //  直接返回false,未被打開
        return false;
    }
 
    public URL getURL() throws IOException {        //  留給子類重寫
        throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");
    }
 
    public URI getURI() throws IOException {   //返回url
        URL url = getURL();
        try {
            return ResourceUtils.toURI(url);     //將url格式化后返回
        }
        catch (URISyntaxException ex) {
            throw new NestedIOException("Invalid URI [" + url + "]", ex);
        }
    }
 
    public File getFile() throws IOException {     //  留給子類重寫
        throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
    }
 
    // 這個資源內容長度實際就是資源的字節長度,通過全部讀取一遍來判斷。這個方法調用起來很占資源啊!
    public long contentLength() throws IOException {   
        InputStream is = this.getInputStream();
        Assert.state(is != null, "resource input stream must not be null");   //斷言
        try {
            long size = 0;
            byte[] buf = new byte[255];
            int read;
            while((read = is.read(buf)) != -1) {
                size += read;
            }
            return size;
        }
        finally {
            try {
                is.close();
            }
            catch (IOException ex) {
            }
        }
    }
 
    public long lastModified() throws IOException {    // 返回資源的最后修改時間
        long lastModified = getFileForLastModifiedCheck().lastModified();
        if (lastModified == 0L) {
            throw new FileNotFoundException(getDescription() +
                    " cannot be resolved in the file system for resolving its last-modified timestamp");
        }
        return lastModified;
    }
 
    // 這是Resource接口所沒有的方法,注釋的意思是“返回文件,給時間戳檢查”,要求子類重寫...
    protected File getFileForLastModifiedCheck() throws IOException {
        return getFile();
    }
 
    public Resource createRelative(String relativePath) throws IOException {   //  留給子類重寫
        throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());
    }
 
    public String getFilename() {  //  默認返回空(假設資源沒有文件名),除非子類重寫
        return null;
    }
 
    @Override
    public String toString() {     //  toString返回文件描述
        return getDescription();
    }
 
    @Override
    public boolean equals(Object obj) {    //  equals比較的就是2個資源描述是否一樣
        return (obj == this ||
            (obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));
    }
 
    @Override
    public int hashCode() {    //  返回資源描述的HashCode
        return getDescription().hashCode();   
    }
 
}復制代碼

 

結論:

  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建立連接來查看。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM