ResouceUtils.getFile()取不到Jar中資源文件源碼小結


Spring提供了一個工具類可以加載classpath下的文件,一般情況下無任何問題,但是當它作為公共的jar包中的工具來加載jar包中的文件時則報出找不到文件的錯誤.

點開看了一下這個工具類ResouceUtils.getFile()方法的源碼:

public static File getFile(String resourceLocation) throws FileNotFoundException {
        Assert.notNull(resourceLocation, "Resource location must not be null");
        if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
            String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length());
            String description = "class path resource [" + path + "]";
            ClassLoader cl = ClassUtils.getDefaultClassLoader();
            URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));
            if (url == null) {
                throw new FileNotFoundException(description +
                        " cannot be resolved to absolute file path because it does not exist");
            }
            return getFile(url, description);
        }
        try {
            // try URL
            return getFile(new URL(resourceLocation));
        }
        catch (MalformedURLException ex) {
            // no URL -> treat as file path
            return new File(resourceLocation);
        }
    }

看了一下代碼結構簡單邏輯清晰,可能有問題的也就是上圖標紅的2處.這里我第一印象是類加載器加載資源的時候沒加載到.Debug了一下cl.getResource(path)用的類加載器是

WebAppClassLoader,想看一下內部實現,但是到這里就跟不進去了,然后百度了一下發現這個是Jetty實現的自己的ClassLoader,截取部分關鍵的加載源碼:

public void addJars(Resource lib)  {  
    if (lib.exists() && lib.isDirectory())  
    {  
        String[] files=lib.list();
        for (int f=0;files!=null && f<files.length;f++)  {  
            try {  
                Resource fn=lib.addPath(files[f]);
                String fnlc=fn.getName().toLowerCase();  
                if (fnlc.endsWith(".jar") || fnlc.endsWith(".zip"))  {  
                    String jar=fn.toString();    
                    jar=StringUtil.replace(jar, ",", "%2C");  
                    jar=StringUtil.replace(jar, ";", "%3B");  
                    addClassPath(jar);  
                }  
            }  catch (Exception ex) {  
                Log.warn(Log.EXCEPTION,ex);  
            }  
        }  
    }  
} 

上面這塊是把jar和zip的path加到類加載器路徑中的部分源碼.繼續debug得到

URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));

上面的url結果:

 

實際上取到了要加載的文件路徑,由於在jar包中,協議字段被標識為jar.到這里看來並非類加載器導致的加載文件失敗.那只好繼續debug往下看getFile()的源碼:

public static File getFile(URL resourceUrl, String description) throws FileNotFoundException {
        Assert.notNull(resourceUrl, "Resource URL must not be null");
        if (!URL_PROTOCOL_FILE.equals(resourceUrl.getProtocol())) {  //URL_PROTOCOL_FILE="file" throw new FileNotFoundException(
                    description + " cannot be resolved to absolute file path " +
                    "because it does not reside in the file system: " + resourceUrl);
        }
        try {
            return new File(toURI(resourceUrl).getSchemeSpecificPart());
        }
        catch (URISyntaxException ex) {
            // Fallback for URLs that are not valid URIs (should hardly ever happen).
            return new File(resourceUrl.getFile());
        }
    }

看到這里有點無語了,上面紅色字體的部分實際判斷資源路徑的協議是否為file,由於在jar包中,協議是jar,故到此處直接拋出文件未找到的異常,

頓時覺得自己好傻,debug這么久,又理了一遍類加載的過程,其實問題是很簡單的一個問題,ResouceUtils.getFile()是專門用來加載非壓縮和Jar包文件類型的資源,所以它根本不會

去嘗試加載Jar中的文件,要想加載Jar中的文件,只要用可以讀取jar中文件的方式加載即可,比如 xx.class.getClassLoader().getResouceAsStream()這種以流的形式讀取文件的方式.

 


免責聲明!

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



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